转义_wsystem中的引号

时间:2017-06-19 18:51:39

标签: c++ windows

我有以下代码用于从程序中运行批处理脚本:

#include <string>
#include <tchar.h>
#include <iostream>

int main()
{
    std::wstring command = _T("\"C:\\cygwin64\\home\\ravik\\path with space\\sample.bat\"");
    std::wstring arg1 = _T("C:\\cygwin64\\home\\ravik\\another_space\\test");
    std::wstring arg2 = _T("\"C:\\cygwin64\\home\\ravik\\another space\\test\"");


    std::wstring fullCommand1 = command + _T(" ") + arg1;

    _wsystem(fullCommand1.c_str());

    std::wstring fullCommand2 = command + _T(" ") + arg2;

    _wsystem(fullCommand2.c_str());

    std::cin.get();

    return 0;
}

fullCommand1fullCommand2中,我使用相同的命令(即sample.bat),但将不同的命令行参数传递给所述命令(arg1 vs { {1}})。但是,虽然arg2命令在使用_wsystem时按预期工作,但当我使用fullCommand1时,我收到错误

  

'C:\ cygwin64 \ home \ ravik \ path'未被识别为内部或外部命令,可操作程序或批处理文件。

看来我的fullCommand2周围的"在第二次调用path with space时消失了。造成这种情况的原因是什么?

1 个答案:

答案 0 :(得分:3)

为什么会这样?

在Microsoft C运行时的system()和_wsystem()实现中存在一个设计缺陷,在这种情况下会使你绊倒。

您尝试运行的命令如下所示:

"c:\path with spaces\my.bat" "argument with spaces"

C运行时采用最简单的方法,将其转换为

cmd /c "c:\path with spaces\my.bat" "argument with spaces"

不幸的是,当请求命令中的第一个字符是引号时,cmd.exerather arcane rules,在这种情况下最终会将命令解释为

c:\path with spaces\my.bat" "argument with spaces

由于显而易见的原因无效。

(我说这是一个设计缺陷,因为如果运行时使用/ S开关并在命令周围添加引号,它将在每种情况下都能正常工作。出于向后兼容性原因,这可能无法修复。或许它可能是只是不是运行时开发团队的优先事项。)

无论原因是什么,结果是你必须自己处理问题。

如何解决此特定情况?

在这种特殊情况下,最简单的解决方案是使用如下命令:

""c:\path with spaces\my.bat" "argument with spaces""

运行时会将其转换为

cmd /c ""c:\path with spaces\my.bat" "argument with spaces""

将被解释为所需,因为有两个以上的引号。在使用这种方法时,您只需要小心一点,因为它可能会导致混淆,即有时只需要额外的引号。

(即使不需要额外的引号,几乎总是安全的,但是如果可执行文件的路径与命令行参数相结合,则存在潜在的歧义碰巧形成了一个通往不同可执行文件的有效路径。请参阅上面链接的答案的附录中的一个例子,尽管是一个相当人为的!)

如何解决一般情况?

最常见的解决方案是将命令字符串指定为

cmd /s /c ""c:\path with spaces\my.bat" "argument with spaces""

运行时将其转换为

cmd /c cmd /s /c ""c:\path with spaces\my.bat" "argument with spaces""

将运行

cmd /s /c ""c:\path with spaces\my.bat" "argument with spaces""

将被解释为

"c:\path with spaces\my.bat" "argument with spaces"

根据需要;由于/ S标志,即使您运行的命令没有任何引号,此变体也将始终按预期工作。

这种方法确实意味着有两个cmd.exe被启动的实例,一个在另一个内;如果这种轻微的开销是不可接受的,你总是可以选择直接使用CreateProcess而不是调用system()。

(另一种最好自己调用CreateProcess的情况是你需要在命令行上使用插入符号或其他特殊字符。)