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

Batch file 如何在for循环中处理路径名中的右括号?,batch-file,for-loop,cmd,quotes,Batch File,For Loop,Cmd,Quotes,我有一个必须在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) …其中“list”是传递给我的程序的参数。我得到错误“'C:\Documents'未被识别为内部或外部

我有一个必须在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)
…其中“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)
…没有更好的结果。我试图像这样逃跑,或者像这样逃跑,没什么可做的。尝试将双引号加倍:

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)
还是不行

此外,事实上,我正在使用一个保存路径的变量,该路径是事先不知道的(从%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
还是不行,不明白为什么

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

for /f "tokens=1" %%G in ('""!_var!"" list') do (echo %%G)
确实有效。。。为什么?为什么要这么做???它有什么影响?我不明白。我还担心它在某些特定情况下可能会造成问题


有什么想法吗?

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

XP中存在已知的FOR/F错误:。但是这个问题与这个bug无关

实际问题源于FOR/F如何执行in()子句中的命令。它使用
CMD\C命令
(请参阅)

通过将这一行添加到Aacini的PROG.BAT示例中,可以观察到这种行为

echo cmdcmdline=%cmdcmdline%
下一期将讨论CMD如何处理/C命令中出现的引号,以及为什么XP的行为与较新的Windows版本不同

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

for /f "delims=" %a in ('"test (this)\prog" args') do @echo %a
FOR尝试执行的命令(%cmdline%)在两个版本中相同(忽略%COMSPEC%中的差异):

XP在处理引号时存在CMD设计缺陷。该缺陷甚至被记录在案(但未被视为缺陷)。Vista和beyond部分修复了设计缺陷,但不必修改文档

以下是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.
这应该是相当明显的失败原因

我想不出规则1中存在特殊角色约束的任何原因。它违背了微软试图做的事情的全部目的

很明显,Vista和更高版本部分修复了设计缺陷,但他们尚未更新帮助文档。Vista忽略特殊字符
,并使用规则1处理命令,保留引号,一切正常

更新2015-05-17:不幸的是,Vista和beyond仍然将
@
^
&
视为特殊字符,即使它们是文件名中的有效字符。当然,
|
被视为特殊字符,但它们在文件名中无论如何都无效。因此,对于Vista和更高版本,规则1的文档应该是
,其中special是一个:&@^ |

我已经追踪了每个人都记录过的行为,所有这些都与上述内容一致

有一种方法可以在XP上执行该命令,而无需使用延迟扩展变量,并且它与Vista及更高版本兼容

for /f "delims=" %a in ('^""test (this)\prog" args^"') do @echo %a
开始引号和结束引号被转义,以便
不会干扰FOR解析器。为IN()子句执行的命令是

C:\Windows\system32\cmd.exe /c ""test (this)\prog" args"
XP和Vista都遵循规则2,因为有两个以上的引号,所以执行CMD

"test (this)\prog" args
一切正常

这个答案的其余部分已经过时,但保留下来是为了给现有评论提供上下文


您的第一个代码示例应该可以工作;它不能(使它不应该)给出您描述的错误消息。错误消息在第一个空格处中断路径,这意味着该路径未被引用或转义。但你“肯定”它被引用了

问题的关键在于文章末尾的三条信息:

  • 实际上,您正在使用具有延迟扩展的变量

  • 这不起作用:
    for/f“tokens=1”%%G在(“!\u var!”列表)中做(echo%%G)

  • 这是有效的:
    for/f“tokens=1”%%G在(“!\u var!”“列表”)中做(echo%%G)

  • 如果var的值已经被引用,那么您将得到所描述的行为

    var的值必须是
    “C:\Documents and Settings\myaccount\Desktop\Test\u release(x86)\program.exe”
    ,包括引号

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

    “!var!”
    失败,因为它扩展到
    ”测试(this)\prog.exe“”
    ,这将有效地取消引用路径。字符串有三个区域,两个被引用,中间的一个不是:

    空引号区域“非引号路径”空引号区域

    “”!瓦尔!“
    之所以有效,是因为它扩展为
    ”test(this)\prog.exe”“”
    ,并且现在再次引用路径。现在,字符串中有五个区域:

    空引用区域“空未引用区域”引用路径“空未引用区域”空引用区域

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

    如果var的值已经被引用,那么只需使用
    !瓦尔编辑-这在XP上不起作用:“!var!”w
    
    C:\Windows\system32\cmd.exe /c ""test (this)\prog" args"
    
    "test (this)\prog" args
    
    >dir /b
    test (this)
    >dir /b "test (this)"
    prog.bat
    >type "test (this)\prog.bat"
    @echo off
    echo/One - Line one of prog
    echo/Two - Line two of prog
    echo/Three - Line three of prog
    
    >for /f "usebackq tokens=1" %G in ("test (this)\prog.bat") do @echo %G
    @echo
    echo/One
    echo/Two
    echo/Three
    
    >for /f "usebackq tokens=1" %G in (`"test (this)\prog.bat"`) do @echo %G
    'test' is not recognized as an internal or external command,
    operable program or batch file.
    
    >type test.bat
    @echo off
    echo Test.batFileLine1
    echo Test.batFileLine2
    echo Test.batFileLine2
    
    >for /f "usebackq tokens=1" %G in (`"test (this)\prog.bat"`) do @echo %G
    Test.batFileLine1
    Test.batFileLine2
    Test.batFileLine2
    
    >for /f "usebackq tokens=1" %G in (`test (this)\prog.bat`) do @echo %G
    \prog.bat`) was unexpected at this time.
    
    >for /f "usebackq tokens=1" %G in (`test ^(this^)\prog.bat`) do @echo %G
    Test.batFileLine1
    Test.batFileLine2
    Test.batFileLine2