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

问题描述 投票:5回答:2

我有一个程序的长路径名,我必须在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)

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

任何的想法?

batch-file for-loop cmd quotes
2个回答
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及其他版本仍将@^&视为特殊字符,即使它们是文件名中的有效字符。当然<>|被视为特殊字符,但无论如何它们在文件名中都无效。因此对于Vista及更高版本,规则1的文档应该读取where special is one of: &<>@^|

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

有一种方法可以在不使用延迟扩展变量的情况下在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

一切正常!

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


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

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

  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!"编辑 - 这对XP不起作用:“”!var!“”同时适用于


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

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

© www.soinside.com 2019 - 2024. All rights reserved.