Windows "cmd.exe" 是否以不同方式解析参数?

Does the Windows "cmd.exe" parse arguments differently?

在我看来,Windows cmd.exe 解析参数字符串的方式与 C 编译的 exe 的解析方式不同。

为了说明,cmd /C "echo ok" 正确打印了 "ok"。但是,cmd "/C" "echo ok" 结果是

'"echo ok' is not recognized as an internal or external command,
operable program or batch file.

为了比较,这是一个 C 程序 "CommandArguments.c",它逐行打印参数:

int main(int argc, char *argv[])
{
    int i;
    for (i = 0; i < argc; ++i) {
        printf("%s\n", argv[i]);
    }
}

如果我运行 CommandArguments.exe "/C" "echo ok",它会正确打印

CommandArguments.exe
/C
echo ok

我问这个是因为我正在实施 API 来包装 CreateProcess。在传递给 CreateProcess 之前,我引用并转义了所有输入参数。由于上述问题,它适用于大多数情况,但不适用于 cmd

所以,我想知道为什么 cmd 的行为不同?它的参数解析规则是什么?有没有其他程序也以不同的方式解析参数?

有趣的观察! cmd.exe 真的很不一样!查看 cmd.exe /? 的文档会发现以下内容:

If you specify /c or /k, cmd processes the remainder of string and quotation marks are preserved

通过试验我发现 /C 选项会从 window!

中抛出正常的命令行处理
C:\Users\Lukas>cmd "/C" "ECHO Hallo"
'"echo ok' is not recognized as an internal or external command,
operable program or batch file.

C:\Users\Lukas>cmd /CECHO Hallo
Hallo

C:\Users\Lukas>cmd "/CECHO Hallo"
Hallo"

C:\Users\Lukas>

我的猜测是 cmd.exe 在命令行中找到 /C 后停止使用正常的命令行处理,并简单地将剩余的字符串传递给命令处理器(如文档中所述) ).如果这是真的,除了在包装器中以不同方式处理 cmd.exe 的(丑陋的)变通方法之外,没有解决您的问题的方法。

任何应用程序都可以以任何它认为合适的方式解析命令行。大多数应用程序使用 C 运行time 库解析器,但没有要求这样做。

理想情况下,您的 API 应该要求调用者为命令行提供单个字符串而不是参数数组,因为这是启动 Windows 进程的正确语法。

如果这不可行,您至少应该为调用者提供一个选项,以防目标应用程序需要特殊处理。

至于命令处理器,其解析行为记录在内置帮助中 (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.

这有点乱,但您可以通过提供 /S 开关来简化它。如果你想要 运行 的命令是 [foo] 那么只需使用

cmd /s /c "[foo]"

正如其他答案已经指出的那样,cmd 在遇到 /C/K 开关时停止解析参数,并将整个剩余字符串传递给命令处理器。

为什么?因为 /C//K 之后的字符串被认为是另一个完整的命令行,所以参数 not 属于 cmd.

例如,我们有这样一个命令行:

cmd /S /C del /Q "any file.txt"

所以 /S/C 都是 cmd 的参数,但是 del/Q... 不是。 /C 之后的所有内容都放在一起并作为单独的命令行处理:

del /Q "any file.txt"

参数/Q"any file.txt"属于命令del