【问题标题】:How can I escape an arbitrary string for use as a command line argument in Windows?如何转义任意字符串以用作 Windows 中的命令行参数?
【发布时间】:2011-09-19 15:24:01
【问题描述】:

我有一个字符串列表,我想在单个 Windows 命令行调用中将这些字符串作为参数传递。对于简单的字母数字字符串,只需逐字传递它们就足够了:

> script.pl foo bar baz yes no
foo
bar
baz
yes
no

我了解如果参数包含空格或双引号,我需要反斜杠转义双引号和反斜杠,然后将参数双引号。

> script.pl foo bar baz "\"yes\"\\\"no\""
foo
bar
baz
"yes"\"no"

但是当我尝试使用文字百分号传递参数时,会发生这种情况:

> script.pl %PATH%
C:\Program
Files\PHP\;C:\spaceless\perl\bin\;C:\Program
Files\IBM\Java60\bin;
(...etc.)

双引号不起作用:

> script.pl "%PATH%"
C:\Program Files\PHP\;C:\spaceless\perl\bin\;C:\Program Files\IBM\Java60\bin; (...etc.)

也没有反斜杠转义(注意反斜杠是如何出现在输出中的):

> script.pl \%PATH\%
\%PATH\%

此外,反斜杠转义反斜杠的规则不一致:

> script.pl "\\yes\\"
\\yes\
> script.pl "\yes\\"
\yes\
> script.pl "\yes\"
\yes"

此外,毫无疑问,Windows 命令行 shell 中存在特殊字符,就像所有 shell 中一样。 那么,安全转义任意命令行参数以在 Windows 命令行中使用的一般过程是什么?

理想的答案将描述一个函数escape(),它可用于以下情况(Perl 示例):

$cmd = join " ", map { escape($_); } @args;

这里还有一些示例字符串应该被这个函数安全转义(我知道其中一些看起来像 Unix,这是故意的):

yes
no
child.exe
argument 1
Hello, world
Hello"world
\some\path with\spaces
C:\Program Files\
she said, "you had me at hello"
argument"2
\some\directory with\spaces\
"
\
\\
\\\
\\\\
\\\\\
"\
"\T
"\\T
!1
!A
"!\/'"
"Jeff's!"
$PATH
%PATH%
&
<>|&^
()%!^"<>&|
>\\.\nul
malicious argument"&whoami
*@$$A$@#?-_

【问题讨论】:

    标签: windows shell cmd


    【解决方案1】:

    这是一个 msdn 博客文章,展示了如何。然而,它假设每个命令行程序在内部使用CommandLineToArgvW 来解析它的命令行(不是一个破旧的假设,因为它是 Shell32 库的一部分)。

    原始链接(可能无效):http://blogs.msdn.com/b/twistylittlepassagesallalike/archive/2011/04/23/everyone-quotes-arguments-the-wrong-way.aspx

    网页存档链接:https://web.archive.org/web/20190109172835/https://blogs.msdn.microsoft.com/twistylittlepassagesallalike/2011/04/23/everyone-quotes-command-line-arguments-the-wrong-way/

    【讨论】:

    • 感谢您提供这个信息丰富的链接。我的问题现在已经回答了。
    • 当然,这似乎只处理命令行参数。相同的过程似乎不适用于转义第零个参数,即要启动的程序的名称:^"C:\Program Files\p.pl^" 产生 '"C:\Program' is not recognized as an internal or external command, operable program or batch file. 有什么想法吗?
    • 看起来support.microsoft.com/kb/103368 暗示了正确的答案。虽然| 不能在文件名中使用,但其他几个命令也是如此。
    【解决方案2】:

    要转义命令行参数,请使用以下命令:

    sub escapeArg {
      my $arg = shift;
    
      # Sequence of backslashes followed by a double quote:
      # double up all the backslashes and escape the double quote
      $arg =~ s/(\\*)"/$1$1\\"/g;
    
      # Sequence of backslashes followed by the end of the arg,
      # which will become a double quote later:
      # double up all the backslashes
      $arg =~ s/(\\*)$/$1$1/;
    
      # All other backslashes do not need modifying
    
      # Double-quote the whole thing
      $arg = "\"".$arg."\"";
    
      # Escape shell metacharacters
      $arg =~ s/([()%!^"<>&|;, ])/\^$1/g;
    
      return $arg;
    }
    

    要转义实际的命令行command,例如在调用具有荒谬名称的命令时,例如()!&amp;%PATH%^;, .exe(这是完全合法的),请使用以下命令:

    sub escapeCmd {
      my $arg = shift;
    
      # Escape shell metacharacters
      $arg =~ s/([()%!^"<>&|;, ])/\^$1/g;
      return $arg;
    }
    

    请注意,使用escapeArg() 命令将不起作用。

    来源:

    【讨论】:

    • 请注意,如果$arg 不包含空格,则从技术上讲,您不需要将$arg 括在双引号内(并且尾随反斜杠的数量加倍)。见Parsing C++ Command-Line Arguments。我也不相信你必须转义 shell 元字符,例如您将命令行直接传递给CreateProcess 并且不涉及cmd(尽管在批处理文件和/或cmd /c/cmd /k 的参数中,您确实需要转义shell元字符.)
    猜你喜欢
    • 2011-09-12
    • 1970-01-01
    • 1970-01-01
    • 2011-07-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-10-18
    • 2023-04-01
    相关资源
    最近更新 更多