在Perl中使用system()调用时,是否必须转义shell args,还是自动完成?
参数将是用户输入,因此我想确保这不会被利用。
答案 0 :(得分:37)
如果使用system $cmd, @args
而不是system "$cmd @args"
(数组而不是字符串),那么您不必转义参数,因为没有调用shell(请参阅system)。即使$ cmd包含元字符且@args为空(exec的一部分记录),system {$cmd} $cmd, @args
也不会调用shell。如果args来自用户输入(或其他不受信任的来源),您仍然需要解开它们。请参阅perlrun文档中的-T
和perlsec文档。
如果您需要阅读输出或向命令发送输入,qx
和readpipe
没有相应的内容。相反,使用open my $output, "-|", $cmd, @args
或open my $input, "|-", $cmd, @args
虽然这不可移植,因为它需要一个真正的fork
,这意味着只有Unix ......我想。也许它可以在Windows上使用它的模拟分支。更好的选择是IPC::Run,它也可以处理将命令发送到其他命令的情况,系统的多参数形式和4参数形式的open都不会处理。
答案 1 :(得分:13)
在Windows上,情况有点糟糕。基本上,所有Win32程序都会收到一个长命令行字符串 - shell(通常是cmd.exe
)可以先做一些解释,例如删除<
和>
重定向,但它确实< em> not 将它拆分为程序的字边界。每个程序都必须自己进行解析(如果他们愿意 - 有些程序不会打扰)。在C和C ++程序中,随编译器工具链提供的运行时库提供的例程通常会在调用main()
之前执行此解析步骤。
问题是,通常,您不知道给定程序将如何解析其命令行。许多程序是使用某些版本的MSVC ++编译的,其quirky parsing rules are described here,但许多其他程序使用不同的编译器编译,使用不同的约定。
cmd.exe
有自己古怪的解析规则,这使事情更加复杂。插入符号(^
)被视为引用后续字符的转义字符,如果满足棘手条件列表,则双引号内的文本将被视为引用(有关完整的详细信息,请参阅cmd /?
)。如果你的命令包含任何奇怪的字符,那么cmd.exe
很容易知道哪些部分的文本被“引用”,哪些不会与你的目标程序不同步,而且所有的地狱都会破裂。
因此,在Windows上转义参数最安全的方法是:
^
为结果字符串前缀每个非字母数字字符。&&
加入命令)。system()
或反引号运行命令。答案 2 :(得分:2)
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
答案 3 :(得分:1)
如果使用系统“$ cmd @args”(字符串),则必须转义参数,因为调用了shell。
幸运的是,对于双引号字符串,只需要转义四个字符:
" - double quote
$ - dollar
@ - at symbol
\ - backslash
答案 4 :(得分:0)
你问题的答案非常有用。最后,我按照@ runrig的建议,然后使用核心模块open3()命令,这样我就可以捕获STDERR和STDOUT的输出。
对于与@ runrig解决方案一起使用的open3()的示例代码,请参阅我的相关问题和答案:
Calling system commands from Perl