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,起初只有参数; switches 和 parameters 的分离主要在后期完成,并且很大程度上取决于应用程序... @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 的实用程序? [关闭]