在Perl中使用 system()调用时,是否必须转义shell args,还是自动完成的?

参数将是用户输入,因此我想确保这不会被利用。

有帮助吗?

解决方案

如果使用system $cmd, @args而不是system "$cmd @args"(数组而不是字符串),则不必转义参数,因为没有调用shell(请参阅系统)。即使$ cmd包含元字符且@args为空,system {$cmd} $cmd, @args也不会调用shell(这是作为 exec )。如果args来自用户输入(或其他不受信任的来源),您仍然需要解开它们。请参阅 perlrun 文档中的-T,以及 perlsec docs。

如果您需要读取输出或向命令发送输入,qxreadpipe没有等效项。相反,使用open my $output, "-|", $cmd, @argsopen my $input, "|-", $cmd, @args虽然这不是可移植的,因为它需要一个真正的fork,这意味着只有Unix ......我想。也许它可以在Windows上使用它的模拟分支。一个更好的选择是 IPC :: Run ,它也将处理案例管道命令到其他命令,系统的多参数形式和4参数形式的open都不会处理。

其他提示

在Windows上,情况有点糟糕。基本上,所有Win32程序都接收一个长命令行字符串 - shell(通常是cmd.exe)可以先做一些解释,例如删除<>重定向,但它将其拆分为程序的字边界。每个程序都必须自己进行解析(如果他们愿意 - 有些程序不会打扰)。在C和C ++程序中,由编译器工具链提供的运行时库提供的例程通常会在调用main()之前执行此解析步骤。

问题是,通常,您不知道给定程序将如何解析其命令行。许多程序是使用某些版本的MSVC ++编译的,其古怪的解析规则在此处描述,但许多其他编译器使用不同的编译器编译,使用不同的约定。

事实上,^有自己古怪的解析规则。插入符号(cmd /?)被视为引用以下字符的转义字符,如果满足棘手条件列表,则双引号内的文本将被视为引用(有关完整的详细信息,请参阅&&)。如果你的命令包含任何奇怪的字符,那么system()知道文本的哪些部分<!>引用<!>引用很容易。并且不会与你的目标程序不同步,而且一切都会破裂。

因此,在Windows上转义参数最安全的方法是:

  1. 以您正在调用的程序的命令行解析逻辑所期望的方式转义参数。 (希望你知道那个逻辑是什么;如果不是,试试几个例子并猜测。)
  2. 使用空格加入转义参数。
  3. 使用<=>前缀结果字符串的每个非字母数字字符
  4. 附加任何重定向或其他shell欺骗(例如,使用<=>加入命令)。
  5. 使用<=>或反引号运行命令。
 sub esc_chars {
  # will change, for example, a!!a to a\!\!a
     @_ =~ s/([;<>\*\|`&\$!#\(\)\[\]\{\}:'"])/\\$1/g;
     return @_;
  }

http: //www.slac.stanford.edu/slac/www/resource/how-to-use/cgi-rexx/cgi-esc.html

如果您使用系统<!>“$ cmd @args <!>”; (一个字符串),然后你必须转义参数,因为调用了一个shell。

幸运的是,对于双引号字符串,只需要转义四个字符:

"    - double quote
$    - dollar
@    - at symbol
\    - backslash

你问题的答案非常有用。最后,我按照@ runrig的建议,然后使用核心模块open3()命令,这样我就可以捕获STDERR和STDOUT的输出。

对于与@ runrig解决方案一起使用的open3()的示例代码,请参阅我的相关问题和答案:
从Perl调用系统命令

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top