如何处理for循环中路径名中的右括号?

phi*_*ilz 5 quotes for-loop cmd batch-file

我有一个程序的长路径名,我必须在for/f循环中运行,其中包括一个右括号")",我需要从中解析输出:

for /f "tokens=1" %%G in ('"C:\Documents and Settings\myaccount\Desktop\Test_release (x86)\program.exe" list') do (echo Will do something with %%G)
Run Code Online (Sandbox Code Playgroud)

...'list'是传递给我程序的参数.我收到错误"'C:\ Documents'不被识别为内部或外部命令,可操作程序或批处理文件."

我知道问题是,右括号实际上关闭了"for"块,因此结尾的双引号不是"看到"的,所以长路径名不再包含在双引号内.我不明白的是为什么会发生这种情况,因为我的路径是用双引号括起来的?我也尝试了usebackq选项:

for /f "usebackq tokens=1" %%G in (`"C:\Documents and Settings\myaccount\Desktop\Test_release (x86)\program.exe" list`) do (echo Will do something with %%G)
Run Code Online (Sandbox Code Playgroud)

......没有更好的结果.我试图逃避这样的"^)"或像这样的"^^)",无所事事.尝试加倍双引号:

for /f "tokens=1" %%G in ('""C:\Documents and Settings\myaccount\Desktop\Test_release (x86)\program.exe"" list') do (echo Will do something with %%G)
Run Code Online (Sandbox Code Playgroud)

仍然无法正常工作.

此外,我实际上使用了一个保存路径的变量,这个变量事先不知道(从%CD%构建),并且激活了EnableDelayedExpansion.我尝试了延迟扩展(在其他情况下修复了类似的问题)以防止变量在读取时扩展并在执行时延迟它:

setlocal EnableDelayedExpansion
set _var=%CD%\program.exe
@REM _var now contains C:\Documents and Settings\myaccount\Desktop\Test_release (x86)\program.exe
for /f "tokens=1" %%G in ('"!_var!" list') do (echo %%G)
endlocal
Run Code Online (Sandbox Code Playgroud)

仍然不起作用,不明白为什么.

但是,在上面代码中延迟扩展的双引号加倍:

for /f "tokens=1" %%G in ('""!_var!"" list') do (echo %%G)
Run Code Online (Sandbox Code Playgroud)

确实有效!......为什么......为什么要这样做?它有什么影响?我不明白.我也担心在某些特定情况下它可能会引起问题......

任何的想法?

dbe*_*ham 5

这个问题的答案中的注释表明XP提供的行为与较新的Windows版本不同.

XP中有一个已知的FOR/F错误:http://www.dostips.com/forum/viewtopic.php?p = 9062#p9062.但是这个问题与那个bug无关.

实际问题源于FOR/F如何在IN()子句中执行命令.它使用CMD \C command(请参阅Windows命令解释器(CMD.EXE)如何解析脚本?)

您可以通过将此行添加到Aacini的PROG.BAT示例来观察此行为.

echo cmdcmdline=%cmdcmdline%
Run Code Online (Sandbox Code Playgroud)

下一个问题涉及CMD如何处理/ C命令中出现的引号,以及为什么XP的行为与更新的Windows版本不同.

此命令在XP中失败,但在Vista及更高版本中取得了成功:

for /f "delims=" %a in ('"test (this)\prog" args') do @echo %a
Run Code Online (Sandbox Code Playgroud)

FOR尝试执行的命令(%cmdcmdline%)在两个版本中都是相同的(忽略%COMSPEC%中的差异):

C:\Windows\system32\cmd.exe /c "test (this)\prog" args
Run Code Online (Sandbox Code Playgroud)

XP在如何处理报价方面存在CMD设计缺陷.该缺陷甚至被记录(但它不被认为是一个缺陷).Vista及其他版本部分修复了设计缺陷,但无需纠正文档.

以下是HELP CMD的摘录

If /C or /K is specified, then the remainder of the command line after
the switch is processed as a command line, where the following logic is
used to process quote (") characters:

    1.  If all of the following conditions are met, then quote characters
        on the command line are preserved:

        - no /S switch
        - exactly two quote characters
        - no special characters between the two quote characters,
          where special is one of: &<>()@^|
        - there are one or more whitespace characters between the
          two quote characters
        - the string between the two quote characters is the name
          of an executable file.

    2.  Otherwise, old behavior is to see if the first character is
        a quote character and if so, strip the leading character and
        remove the last quote character on the command line, preserving
        any text after the last quote character.
Run Code Online (Sandbox Code Playgroud)

我们希望CMD遵循规则1,这样的报价将被保留,但()违反了XP的特殊字符的限制,所以规则2之后和CMD试图执行

test (this)\prog args
Run Code Online (Sandbox Code Playgroud)

这个失败的原因应该是相当明显的!

我无法想到为什么规则1中存在特殊字符约束的任何原因.它违背了MS试图做的所有目的.

显然,设计缺陷在Vista及更高版本中已部分修复,但它们尚未更新HELP文档.Vista忽略特殊字符()使用规则1处理命令,保留引号,一切正常.

更新2015年5月17日: 不幸的是,Vista和超越还是治疗@,^以及&为特殊字符,即使它们的文件名中的有效字符.当然<,>并且|被视为特殊字符,但无论如何它们在文件名中都无效.因此,对于Vista及更高版本,应阅读规则1的文档 where special is one of: &<>@^|.

我已经跟踪了每个人都记录下来的行为,这与上述内容完全一致.

有一种方法可以在不使用延迟扩展变量的情况下在XP上执行命令,并且它与Vista及更高版本兼容.

for /f "delims=" %a in ('^""test (this)\prog" args^"') do @echo %a
Run Code Online (Sandbox Code Playgroud)

开始和结束引号被转义,因此)不会干扰FOR解析器.为IN()子句执行的命令是

C:\Windows\system32\cmd.exe /c ""test (this)\prog" args"
Run Code Online (Sandbox Code Playgroud)

XP和Vista都遵循规则2,因为有两个以上的引号,所以CMD执行

"test (this)\prog" args
Run Code Online (Sandbox Code Playgroud)

一切正常!

此答案的其余部分已过时,但保留为现有注释的上下文.


你的第一个代码示例应该工作; 它不能(应该)给出你描述的错误信息.错误消息中断了第一个空格处的路径,这意味着路径未被引用或转义.但你"确定"它被引用了.

问题的关键在于帖子末尾附近的三条信息:

  1. 实际上正在使用具有延迟扩展的变量

  2. 这不起作用: for /f "tokens=1" %%G in ('"!_var!" list') do (echo %%G)

  3. 这工作: for /f "tokens=1" %%G in ('""!_var!"" list') do (echo %%G)

如果已经引用了var的值,您将获得您正在描述的行为.

var的值必须是"C:\Documents and Settings\myaccount\Desktop\Test_release (x86)\program.exe",包括引号.

为了使这个解释更具可读性,我将缩短其路径 "test (this)\prog.exe"

"!var!"失败,因为它扩展到""test (this)\prog.exe"",有效地取消引用路径.该字符串有三个区域,两个引用,一个中间不是:

" 空引用区 "未引用路径" 空引区 "

""!var!""因为它扩展到"""test (this)\prog.exe"""并且路径现在再次被引用而起作用.字符串中现在有五个区域:

" 空引用区域 "空未引用区域" 引用路径 "空未引用区域 " 空引用区域 "

关于你应该如何进行的简单答案:

如果已经引用了var的值,那么只需使用!var! 在XP上不起作用的编辑:"!var!" 适用于两者

如果没有引用var的值,那么使用"!var!" Edit-对XP不起作用:""!var!""同时适用于