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