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

时间:2011-11-18 21:44:02

标签: 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”块,所以结尾双引号不是“看到”,所以长路径名不包含在double中引用了。我不明白的是为什么会发生这种情况,因为我的路径是用双引号括起来的?我也尝试了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)

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

有什么想法吗?

2 个答案:

答案 0 :(得分:5)

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

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

实际问题源于FOR / F如何在IN()子句中执行命令。它使用CMD \C command(请参阅How does the Windows Command Interpreter (CMD.EXE) parse scripts?

您可以通过在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尝试执行的命令(%cmdcmdline%)在两个版本中都是相同的(忽略%COMSPEC%中的差异):

C:\Windows\system32\cmd.exe /c "test (this)\prog" args

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.

我们希望CMD遵循规则1以保留引号,但()违反XP上的特殊字符约束,因此遵循规则2并且CMD尝试执行

test (this)\prog args

为什么失败应该是相当明显的!

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

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

更新2015-05-17: 不幸的是,Vista及更高版本仍会处理@^和{{1作为特殊字符,即使它们是文件名中的有效字符。当然&<>被视为特殊字符,但无论如何它们在文件名中都无效。因此,对于Vista及更高版本,规则1的文档应为 |

我已经跟踪了每个人都记录的行为,并且与上述行为一致。

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

where special is one of: &<>@^|

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

)

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

C:\Windows\system32\cmd.exe /c ""test (this)\prog" args"

一切正常!

此答案的其余部分已过时,但保留为现有评论的上下文。


你的第一个代码示例应该可行;它不能(不应该这样做)给出你描述的错误信息。错误消息中断了第一个空格处的路径,这意味着路径未被引用或转义。但是你确定&#34;肯定&#34;它被引用了。

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

  1. 实际使用延迟扩展的变量

  2. 这不起作用:"test (this)\prog" args

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

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

    您的var的值必须为for /f "tokens=1" %%G in ('""!_var!"" list') do (echo %%G),包括引号。

    为了使这个解释更具可读性,我将缩短"C:\Documents and Settings\myaccount\Desktop\Test_release (x86)\program.exe"

    的路径

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

      

    &#34; 空引用区域&#34;未引用的路径&#34; 空引用区域&#34;

    ""test (this)\prog.exe""有效,因为它会扩展为""!var!"",现在再次引用该路径。现在字符串中有五个区域:

      

    &#34; 空引用区域&#34;空未引用区域&#34; 引用路径&#34;清空未引用区域&#34; 为空引用区域&#34;

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

    如果已经引用了var的值,那么只需使用"""test (this)\prog.exe""" 编辑 - 这对XP不起作用:&#34;!var!&#34;适用于

    如果未引用var的值,则使用!var! 编辑 - 这不适用于XP:&#34;&#34;!var!&# 34;&#34;适用于

答案 1 :(得分:1)

我做了一些测试,以找出为什么你最初使用的直接方法不起作用。我首先创建了一个名为test (this)的目录,在其中我创建了一个名为prog.bat的批处理文件:

>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访问此类文件的内容。请注意,useback选项是必需的,因为文件名包含空格,并且必须用引号括起来:

>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.

然后,因为消息说test是未找到命令的名称,我创建了一个名为test.bat的批处理文件:

>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

啊哈!以前的结果表明,在这种情况下,引号会被忽略,只执行test (this)\prog.bat,对吗?

>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

我的结论是for /f命令在反引号和引号与“usebackq”选项结合使用且文件名有空格时出错,并且如果延迟变量扩展则忽略此错误使用。