Windows“cmd.exe”是不是以不同方式解析参数?

Posted

技术标签:

【中文标题】Windows“cmd.exe”是不是以不同方式解析参数?【英文标题】:Does the Windows "cmd.exe" parse arguments differently?Windows“cmd.exe”是否以不同方式解析参数? 【发布时间】:2016-03-30 14:57:56 【问题描述】:

在我看来,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 的行为不同?它的参数解析规则是什么?有没有其他程序也以不同的方式解析参数?

【问题讨论】:

不要将“命令参数”(“参数”)与“命令开关”混淆。 @Stephan,起初只有参数switchesparameters 的分离主要在后期完成,并且很大程度上取决于应用程序... @aschipfl 开关是告诉命令的东西,做什么,一个参数/参数告诉它,有什么数据。将参数括在 qoutes 中将其保留为数据,将 Switch 括在引号中,将其更改为数据。 @Stephan:理论上,引号中的切换被解释为数据是有意义的,但大多数应用程序都不会费心去区分。 (但如果他们愿意,他们可以,这是 Windows 传递整个命令行模型的优点之一。) 【参考方案1】:

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

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

如果这不可行,您至少应该为调用者提供一个选项,以便在目标应用程序需要特殊处理的情况下这样做。

至于命令处理器,它的解析行为记录在内置帮助(cmd /?)中:

如果指定了 /C 或 /K,则命令行后面的剩余部分 开关作为命令行处理,其中以下逻辑是 用于处理引号 (") 字符:

    如果满足以下所有条件,则引用字符 在命令行上保留:

    没有 /S 开关 正好两个引号字符 两个引号字符之间没有特殊字符, 其中 special 是以下之一:&()@^| 之间有一个或多个空白字符 两个引号字符 两个引号字符之间的字符串是名称 一个可执行文件。

    否则,旧行为是查看第一个字符是否为 一个引号字符,如果是这样,去掉前导字符和 删除命令行上的最后一个引号字符,保留 最后一个引号字符之后的任何文本。

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

cmd /s /c "[foo]"

【讨论】:

【参考方案2】:

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

如果指定 /c 或 /k,cmd 会处理字符串的剩余部分并保留引号

实验后我发现/C 选项将正常的命令行处理抛出窗口!

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 的(丑陋的)变通方法之外,您的问题没有解决方案。

【讨论】:

【参考方案3】:

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

为什么?因为/C//K后面的字符串被认为是另一个完整的命令行,所以参数属于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

【讨论】:

说得好。如果您必须弄清楚如何正确引用您正在传递的命令行部分,那将是非常尴尬的。

以上是关于Windows“cmd.exe”是不是以不同方式解析参数?的主要内容,如果未能解决你的问题,请参考以下文章

cmd.exe 是不是有类似 sed 的实用程序? [关闭]

为啥 cmd.exe 以不同方式解析插入符号

使c ++程序以交互方式将输入输出传递给Windows命令提示符

以管理员身份运行cmd

如何设置cmd以管理员身份运行

使用cmd.exe以UTF-8编码保存文本文件