Windows 批处理脚本:\MyProg此时意外出现

Windows 批处理脚本:\MyProg此时意外出现,windows,batch-file,Windows,Batch File,我有一个简单的批处理脚本test.bat,它通过命令行参数进行迭代: @echo off set prefix=C:\Program Files\MyProg for %%x in (%*) do ( echo %prefix% ) 此版本的脚本在cmd.exe中正常工作: C:\>test a b C:\Program Files\MyProg C:\Program Files\MyProg 但是,如果我修改批处理文件使其具有不同的前缀值,则添加(x86): 脚本失败: C:

我有一个简单的批处理脚本test.bat,它通过命令行参数进行迭代:

@echo off
set prefix=C:\Program Files\MyProg
for %%x in (%*) do (
    echo %prefix%
)
此版本的脚本在cmd.exe中正常工作:

C:\>test a b
C:\Program Files\MyProg
C:\Program Files\MyProg
但是,如果我修改批处理文件使其具有不同的
前缀
值,则添加
(x86)

脚本失败:

C:\>test a b
\MyProg was unexpected at this time.
真是一场灾难!如何解决此问题?

使用
启用!前缀解决此问题:

@echo off
set prefix=C:\Program Files (x86)\MyProg
setlocal EnableDelayedExpansion
for %%x in (%*) do (
    echo !prefix!
)
endlocal

!!难以置信

尽管您发现
delayedexpansion
解决了括号内代码块内部的问题,但考虑到现有示例,您不需要代码块,因此可以不使用括号和
delayedexpansion
。还建议双引号引用set变量和值,以消除可能出现的空白:

@echo off
set "prefix=C:\Program Files (x86)\MyProg"
for %%x in (%*) do echo %prefix%
我还假设您也想以某种方式使用
%%x
,因此:

@echo off
set "prefix=C:\Program Files (x86)\MyProg"
for %%x in (%*) do echo %prefix%\%%~x

我建议先读一读

在使用命令块执行命令之前,Windows命令处理器将解析整个代码块,从
开始,以匹配的
结束)
。在处理命令块的过程中,
%variable%
格式中的所有环境变量引用都将被环境变量的当前值替换,或者在一个根本不存在的环境变量上被删除。因此,最终执行的命令块不再包含任何
%variable%
。这种行为很好地解释了

那么代码呢

set prefix=C:\Program Files (x86)\MyProg
for %%x in (%*) do (
    echo %prefix%
)
使用执行的结果

echo C:\Program Files (x86)\MyProg
右括号
不在被括在
中的参数字符串内。因此,Windows命令处理器将其解释为的命令命令块的结尾

被解释为命令块的结尾之后,在同一行上指定一个以上的命令,而不使用诸如
&
&
|
之类的运算符,这在语法上是不正确的。因此
\MyProg
标记结束后被解释为语法上无效的命令f命令块

在命令提示窗口中运行
cmd/?
的帮助输出在最后一页的末尾解释说,包含空格或其中一个字符的文件名
&()[]{}^=;!'+,`
必须包含在
中,才能对所有这些字符进行逐字解释,但
除外如果也启用了延迟环境变量扩展

在参数字符串周围使用
不仅对文件名是必需的,而且对任何包含空格或其中一个字符的参数字符串
&()[]{}^=;!'+,`~
或重定向操作符
也是必需的,它们也应该由
cmd.exe
逐字解释

因此,一个解决方案是:

set prefix=C:\Program Files (x86)\MyProg
for %%x in (%*) do (
    echo "%prefix%"
)
ECHO输出带有两个
的前缀“
,但输出带有ECHO的文件/文件夹名时最好始终用双引号括起来

set "prefix=%ProgramFiles(x86)%\MyProg"
for %%I in (%*) do (
    echo "%prefix%\%%~I"
)
另一种解决方案是转义
,将
^
解释为文字字符。在这种情况下,在将
%prefix%
替换为分配给环境变量
prefix
的字符串值后,带有
echo
的行仍然具有
^)
非常重要。因此,有必要定义带有插入符号的
前缀
字符串,插入符号被解释为文字字符,这意味着在环境变量定义中,有两个
^
是必需的

set prefix=C:\Program Files (x86^^)\MyProg
for %%x in (%*) do (
    echo %prefix%
)
环境变量
前缀
现在使用字符串值定义:

C:\Program Files (x86^)\MyProg
这将导致
FOR
命令块将
)解释为文字字符

set "prefix=C:\Program Files (x86^)\MyProg"
for %%x in (%*) do (
    echo %prefix%
)
还可以定义环境变量
前缀
,并将其括在双引号中,以将插入符号
^
解释为文字字符,从而避免将该字符加倍,否则将其解释为转义字符

set "prefix=C:\Program Files (x86^)\MyProg"
for %%x in (%*) do (
    echo %prefix%
)
另一种解决方案是使用延迟环境变量扩展:

@echo off
set prefix=C:\Program Files (x86)\MyProg
setlocal EnableDelayedExpansion
for %%x in (%*) do (
    echo !prefix!
)
endlocal
但是这个解决方案有一个问题,这是由于命令行的双重解析造成的,因为启用了延迟扩展,这可以在将代码修改为

@echo off
set prefix=C:\Program Files (x86)\MyProg
setlocal EnableDelayedExpansion
for %%x in (%*) do (
    echo !prefix!\%%~x
)
endlocal
并使用三个参数执行批处理文件
Hello
测试
开始。由于启用了延迟扩展,感叹号不再解释为文字字符。因此,输出为:

C:\Program Files (x86)\MyProg\Hello
但结果应该是:

C:\Program Files (x86)\MyProg\Hello!
C:\Program Files (x86)\MyProg\Test
C:\Program Files (x86)\MyProg\Start!
此输出可通过以下方式获得:

set "prefix=C:\Program Files (x86^)\MyProg"
for %%x in (%*) do (
    echo %prefix%\%%~x
)
但是,真正安全的方法是打印包含在双引号中的串联字符串

set "prefix=%ProgramFiles(x86)%\MyProg"
for %%I in (%*) do (
    echo "%prefix%\%%~I"
)
在本例中,上述示例的输出包含三个参数
Hello
测试
开始

"C:\Program Files (x86)\MyProg\Hello!"
"C:\Program Files (x86)\MyProg\Test"
"C:\Program Files (x86)\MyProg\Start!"
另请参见我的回答,其中解释了为什么环境变量
前缀
是使用
左至变量名称并在分配给环境变量的字符串值末尾定义的。这是环境变量定义的推荐语法

此外,最好不要将区分大小写的解释修饰符存在的解释字母用作循环变量,否则结果可能是意外的,尤其是在动态连接的变量字符串上

例如:

@echo off
cls
echo Loop with %%~Xx:
for %%x in ("1" 2 3) do echo %%~Xx run.
echo Loop with %%~xX:
for %%X in ("1" 2 3) do echo %%~xX run.
echo Loop with %%~Ix:
for %%I in ("1" 2 3) do echo %%~Ix run.
echo Loop with %%~#x:
for %%# in ("1" 2 3) do echo %%~#x run.
pause
此小批量文件的输出为:

Loop with %~Xx:
 run.
 run.
 run.
Loop with %~xX:
 run.
 run.
 run.
Loop with %~Ix:
1x run.
2x run.
3x run.
Loop with %~#x:
1x run.
2x run.
3x run.
此处的目的是获取附加了
x
的输出编号,而不是引用FOR的命令集中三个字符串的不存在的文件扩展名

因此,修饰符字母
ADFNPSTXZadfnpstxz
不应用作循环变量,尽管在大多数情况下是可能的