简体   繁体   English

批处理:如何在变量中存储带有空格的命令输出?

[英]Batch: How to store command output with spaces in variable?

I want to make a Batch file to create windows firewall rules for an executable (for example, python).我想制作一个批处理文件来为可执行文件(例如,python)创建 Windows 防火墙规则。 To do this, I have the following script:为此,我有以下脚本:

REM ~ Open ports. Run with elevated administrator rights.
CALL :OPEN "python"
GOTO :EOF

:OPEN
IF ERRORLEVEL 1 (
  GOTO :EOF
)
FOR /F %%i IN ('where %1') DO CALL :OPEN_PROGRAM "%1" %%i && GOTO :EOF
GOTO :EOF

:OPEN_PROGRAM
FOR %%j IN (TCP UDP) DO CALL :OPEN_PROGRAM_PORT %1 %2 "%%j"
GOTO :EOF

:OPEN_PROGRAM_PORT
netsh advfirewall firewall add rule name=%1 description=%1 dir=in action=allow edge=deferuser     profile=private program=%2 protocol=%3
GOTO :EOF

This works fine as long as there are no spaces in the path to the executable.只要可执行文件的路径中没有空格,就可以正常工作。

If there are spaces in the path, then only the part up to the spaces is put in the %%i variable.如果路径中有空格,则仅将空格之前的部分放入%%i变量中。 I tried using 'where /F %1' , but then it still cuts the output on the space.我尝试使用'where /F %1' ,但它仍然会削减空间上的输出。

Is there a way to store the full output of where %1 in a variable?有没有办法将where %1的完整输出存储在变量中? (Preferably without writing it to a file) (最好不要将其写入文件)

I would first advise you not to pass an filename without an extension.我首先建议您不要传递没有扩展名的文件名。 Doing so relies upon each of the extensions listed under %PATHEXT% , and you could have files named python.com , python.bat , python.cmd , python.vbs , python.vbe , python.js , python.jse , python.wsf , python.wsh , and python.msc , as well as python.exe .这样做依赖于%PATHEXT%下列出的每个扩展名,您可以拥有名为python.compython.batpython.cmdpython.vbspython.vbepython.jspython.jsepython.wsfpython.wshpython.msc ,以及python.exe I'm sure you wouldn't want to unknowingly create firewall rules for all of those.我相信您不会在不知不觉中为所有这些创建防火墙规则。

I would also advise that you do not use the where command for this.我还建议您不要为此使用where命令。 The reason being that it could return more than a single item.原因是它可以返回多个项目。 For example, try this, where notepad.exe and if memory serves, you'll probably see, C:\\WINDOWS\\System32\\notepad.exe , followed by C:\\WINDOWS\\notepad.exe .例如,试试这个, where notepad.exe并且如果没记错的话,您可能会看到C:\\WINDOWS\\System32\\notepad.exe ,然后是C:\\WINDOWS\\notepad.exe That is because where.exe searches each location in %PATH% in order of their listing, and returns each, as the first two entries under %PATH% should by default be C:\\WINDOWS\\system32;C:\\WINDOWS;这是因为where.exe按列表顺序搜索%PATH%中的每个位置,并返回每个位置,因为%PATH%下的前两个条目默认应为C:\\WINDOWS\\system32;C:\\WINDOWS; . .

My suggestion therefore would be to use another method, which returns the first item found under %PATH% as opposed to all, and this should, if you've used the variable properly, return the default, (most used) item.因此,我的建议是使用另一种方法,它返回在%PATH%下找到的第一个项目,而不是所有项目,如果您正确使用了变量,这应该返回默认的(最常用的)项目。 The method is the %~$PATH variable expansion, and is documented under the help information for the for command, ie entering for /?方法是%~$PATH变量扩展,在for命令的帮助信息下有说明,即输入for /? at the Command Prompt.在命令提示符处。

Example Script:示例脚本:

@Echo Off
SetLocal EnableExtensions
Rem ~ Open ports. Run with elevated administrator rights.
"%__AppDir__%reg.exe" Query HKU\S-1-5-19 1>NUL 2>&1 || (
 Echo This script must be run elevated.
 Echo Please use 'Run as administrator'
 "%__AppDir__%timeout.exe" /T 3 /NoBreak 1>NUL
 GoTo :EOF)
Call :OpenPorts "python.exe"
Pause
GoTo :EOF
 
:OpenPorts
For %%G In ("%~1") Do For %%H In (TCP UDP) Do (
 "%__AppDir__%netsh.exe" AdvFirewall Firewall Add Rule^
 Name="%~n1" Description="%~1" Dir=in Action=allow^
 Edge=deferuser Profile=private Program="%%~$Path:G"^
 Protocol=%%H)
Exit /B

I have removed all of the unnecessary Call commands, (using just the one) , and deliberately used carets to split up your super long line, for readability;我已经删除了所有不必要的Call命令, (只使用一个) ,并故意使用插入符号来分割你的超长行,以提高可读性; (which also allows you to include the localport= and/or remoteport= option, which I'd assume by your 'Open Ports' name you're going to want to include as a modification to the above later) . (这也允许您包含localport=和/或remoteport=选项,我假设您的“开放端口”名称是您稍后要作为对上述内容的修改而包含的) I also took the liberty of including some code, to determine if the script was being run elevated, and display a message before closing, if it isn't.我还冒昧地包含了一些代码,以确定脚本是否正在运行,如果不是,则在关闭之前显示一条消息。

Write the output of where.exe to a file, then read the file one line at a time.where.exe的输出写入文件,然后一次读取一行。 There is a way to do it without a temp file after the "===" line.有一种方法可以在“===”行之后没有临时文件。

@ECHO OFF
SET "XTOFIND=%~1"
SET "TEMPFILE=%TEMP%\wherelist.tmp"
IF EXIST "%TEMPFILE%" (DEL "%TEMPFILE%")

"%__appdir__%where.exe" "%XTOFIND%" >"%TEMPFILE%"

FOR /F "tokens=*" %%A IN ('TYPE "%TEMPFILE%"') DO (
    ECHO CALL :OPEN_PROGRAM "%%~A" %2 "%%J"
)

IF EXIST "%TEMPFILE%" (DEL "%TEMPFILE%")

ECHO ============

SET "XTOFIND=%~1"
FOR /F "tokens=*" %%A IN ('"%__appdir__%where.exe" "%XTOFIND%"') DO (
    ECHO %%~A
    ECHO CALL :OPEN_PROGRAM "%%~A" %2 "%%J"
)
EXIT /B 0

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM