是Windows" cmd.exe"解析参数不同?

时间:2015-12-24 06:09:54

标签: c windows command-line cmd command-line-arguments

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

为了说明,cmd /C "echo 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表现不同?解析规则的论点是什么?还有其他程序还以不同的方式解析参数吗?

3 个答案:

答案 0 :(得分:3)

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

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

如果这不可行,那么在目标应用程序需要特殊处理的情况下,您应该至少为调用者提供选项

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

  

如果指定了/ C或/ K,那么后面的命令行的其余部分   交换机作为命令行处理,其中包含以下逻辑   用于处理引号(&#34;)字符:

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

         
        
    • 否/ S开关
    •   
    • 正好两个引号字符
    •   
    • 两个引号字符之间没有特殊字符,    special是以下之一:&amp;&lt;&gt;()@ ^ |
    •   
    • 之间有一个或多个空格字符    两个引号字符
    •   
    • 两个引号字符之间的字符串是名称    可执行文件。
    •   
  2.   
  3. 否则,旧行为是查看第一个字符是否为       引用字符,如果是这样,剥去主角和       删除命令行上的最后一个引号字符,保留       最后一个引号字符后面的任何文字。

  4.   

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

cmd /s /c "[foo]"

答案 1 :(得分: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之外,你的问题没有解决办法。

答案 2 :(得分:2)

正如其他答案已经指出的那样,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