如何在 Windows 上的 "for /F" 循环命令中引用空格

How to quote spaces in the command of a "for /F" loop on Windows

我有以下 for 命令

for /F %s in ('"mas ter\gradlew.bat" -q -p mass ter') do echo %s

而且我不能让它正常工作。这不是实际用例,而是一个简化得多的示例。

正在执行

"mas ter\gradlew.bat" -q -p "mas ter"

工作正常,

"mas ter\gradlew.bat" -q -p mass ter

表示项目目录 "mass" 不存在(额外的 "s" 仅用于区分目的),

mas ter\gradlew.bat -q -p mass ter

说找不到命令 "mas"。

到目前为止如预期的那样,但现在我想让它在 for 循环中工作。

for /F %s in ('mas ter\gradlew.bat -q -p mass ter') do echo %s

当然再说了,没找到命令"mas",

for /F %s in ('"mas ter\gradlew.bat" -q -p mass ter') do echo %s

当然再说一遍项目目录 "mass" 不存在但现在它来了,

for /F %s in ('"mas ter\gradlew.bat" -q -p "mass ter"') do echo %s

再次表示未找到命令 "mas"。 o_O

现在我在 wtf。
为什么会发生这种情况,我该如何让它发挥作用?
^ 个字符转义空格也不起作用。
除此之外,这也无济于事,因为这两个路径存储在参数中并像

一样使用
for /F %s in ('"%script_path%\gradlew.bat" -q -p "%script_path%"') do echo %s

这真是令人沮丧,因为命令的解析很奇怪

('"mas ter\gradlew.bat" -q -p mass ter')

但是你不能在第一个参数和任何其他参数中都使用双引号

('"mas ter\gradlew.bat" -q -p "mass ter"')

下面的东西可以正常工作

('master\gradlew.bat -q -p mass ter')

所以我带了一些东西来让它工作

par ams.bat

名称中带有 space 的文件并回显每个参数。我还创建了与 params.bat

相同的副本
@echo off
setlocal enabledelayedexpansion
echo "You sent '%*'"
set argC=0
for %%x in (%*) do (
    Set /A argC+=1
    echo !argC! is %%x
)
echo %argC%

F:\params.sh>"par ams.bat" 1 2 "ab c"
"You sent '1 2 "ab c"'"
1 is 1
2 is 2
3 is "ab c"
3

这显示了我需要做什么。

现在一个文件来测试设置

@echo off

for /F "tokens=* delims=" %%s in (params.bat " -q" "  -p " "abc"`) do echo %%s

效果很好

"You sent '" -q" "  -p " "abc"'"
0 is " -q"
0 is "  -p "
0 is "abc"
3

接下来是尝试 par ams.bat。我尝试了不同的变体

for /F "tokens=* delims=" %%s in ('par ams.bat " -q" "  -p " "abc"') do echo %%s
for /F "tokens=* delims=" %%s in ('"par ams.bat" " -q" "  -p " "abc"') do echo %%s
for /F "tokens=* delims=" %%s in ('par ams.bat' " -q" "  -p " "abc"') do echo %%s
for /F "tokens=* delims= usebackq" %%s in (`'par ams.bat' " -q" "  -p " "abc"`) do echo %%s
for /F "tokens=* delims= usebackq" %%s in (`"par ams.bat" " -q" "  -p " "abc"`) do echo %%s

和 none 上面的工作。所以结论是第一个没有 space 的参数,然后是 space 或引号的任何参数,没有问题。所以我想出了一个额外的批处理文件

escape.bat

@echo off
SET COMMAND=%~1
SHIFT
%COMMAND %%*

该文件什么都不做,只是将第一个参数作为要执行的内容,然后是相同的命令行参数。现在我更新了测试以使用

for /F "tokens=* delims=" %%s in ('escape.bat "par ams.bat" " -q" "  -p " "abc"') do echo %%s

然后成功了

F:\params.sh>test
"You sent '" -q" "  -p " "abc"'"
0 is " -q"
0 is "  -p "
0 is "abc"
3

Edit-1:使用 call 而不是 escape.bat

感谢您对此@Vampire 的反馈。所以更好的方法是使用 call 而不是 escape.bat。您也可以使用 set script_dir=%~dp0 获取脚本目录,然后在批处理文件中使用相同的目录。所以我们会改变

for /F "tokens=* delims=" %%s in ('escape.bat "par ams.bat" " -q" "  -p " "abc"') do echo %%s

set script_name=%~dp0
for /F "tokens=* delims=" %%s in ('call "%script_name%\par ams.bat" " -q" "  -p " "abc"') do echo %%s

执行摘要:

将命令字符串包含在另一组 double-quotes 中,无论命令字符串中是否使用引号(只留下那些):

for /F %s in ('""%script_path%\gradlew.bat" -q -p "%script_path%""') do echo %s

tl;dr 详情:

似乎 for /f ... ('command') 变体只是将命令字符串传递给 cmd /c(或某些等效的内部代码)以实际 运行 命令。

一般来说,cmd.exe 具有复杂、随意且错误的引用处理规则。 cmd /c 的特定规则包括从 cmd /? 帮助文本中复制的这些规则:

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.

(谁想出这些东西的?)

对我来说,这归结为最好只将命令字符串括在一组 double-quotes 中,而不管命令字符串中是否使用引号(不要管引号)。

这是一个批处理文件,位于包含 space - mas ter\gradlew.bat:

的路径中
@echo off
echo "Running '%~dpnx0'"

:echo_args
    if "" == "%1" goto :eof
    echo arg: %1
    shift

这是没有 for 循环的那个批处理文件的 运行:

C:\temp>"mas ter\gradlew.bat" -q -p "mass ter"
"Running 'C:\temp\mas ter\gradlew.bat'"
arg: -q
arg: -p
arg: "mass ter"

这是在一个 for 循环中使用的,它包含一组 double-quotes::

C:\temp>for /F "tokens=*" %s in ('""mas ter\gradlew.bat" -q -p "mass ter""') do @echo from the for loop: "%s"
from the for loop: ""Running 'C:\temp\mas ter\gradlew.bat'""
from the for loop: "arg: -q"
from the for loop: "arg: -p"
from the for loop: "arg: "mass ter""