如何使用system()避免在Perl中意外逃逸?
我想使用命令运行一些命令,我这样做:如何使用system()避免在Perl中意外逃逸?,perl,system,Perl,System,我想使用命令运行一些命令,我这样做: execute_command_error("trash-put '/home/$filename'"); 其中execute\u command\u error将报告它运行的任何system命令是否存在错误。我知道我可以使用Perl命令取消文件链接,但我想使用垃圾箱删除文件,因为这是一种回收程序 我的问题是,$filename中有时会有撇号、引号和其他奇怪的字符,这些字符会弄乱系统命令或Perl本身。将命令名和参数生成为数组,并将其传递给系统: my(@
execute_command_error("trash-put '/home/$filename'");
其中execute\u command\u error
将报告它运行的任何system
命令是否存在错误。我知道我可以使用Perl命令取消文件链接,但我想使用垃圾箱删除文件,因为这是一种回收程序
我的问题是,$filename
中有时会有撇号、引号和其他奇怪的字符,这些字符会弄乱系统
命令或Perl本身。将命令名和参数生成为数组,并将其传递给系统:
my(@command) = ("trash-put", 'home/$filename');
system @command;
这意味着Perl不会调用shell来执行任何元字符扩展(或I/O重定向、命令管道或…)。它确实意味着它完全按照你告诉它的去做
sub execute_command_error
{
system @_;
}
从大量评论中借用信息:
这在perldoc-f系统或(@Ether)中有清楚的记录
(另请参见下文中与之密切相关的“exec”讨论。)
你是想用单引号引$filename吗?(@mobrule)
我确实打算使用单引号-我正在证明$filename
不会被Perl或Shell扩展…在我的测试脚本中,我使用了“my.$file
”,这给了我一个名为$
的文件-正如我所想的那样
我认为如果您确实想要调用shell(例如,如果您想要一些管道),那么所需的引号是$command\u line=“\”$command\“\”$arg1\“\“$arg2\”…”。(@Jefromi)
在参数周围添加双引号对嵌入的$
、backtick1、$(…)
”和相关符号没有帮助。您几乎需要在内容周围使用单引号,但随后需要将嵌入的单引号重写为“”\''
”,这将生成一个单引号以终止当前单引号参数,一个反斜杠引号组合以表示单引号,以及另一个单引号以恢复单引号参数
如果我直接使用系统命令,这将是一个很好的解决方案;然而,我正在使用webmin的execute_command
函数,这有点让我费解,所以我不知道如何编辑它以允许使用数组。您能否将嵌入式单引号的重写扩展为“”\'
”…这就是我现在要使用的内容。(@Brian)
粗略地说,(Unix)shell处理单引号的方式是“从第一个单引号到下一个单引号的所有内容都是文本,没有元字符”。因此,为了让shell将某些内容视为文本,请将其包含在单个字符中。除了单引号本身,它处理所有的事情。正如我的评论所说,您必须使用4个字符的替换字符串来将单引号嵌入到单引号参数的中间
可能有比这更简洁的方法(可能使用一个或两个map
操作),但这应该是可行的:
for (my $i = 0; $i < scalar(@command); $i++)
{
$command[$i] =~ s/'/'\\''/g; # Replace single quotes by the magic sequence
$command[$i] = "'$command[$i]'"; # Wrap value in single quotes
}
或者更直接地说
当参数通过系统shell执行时,结果取决于它的特性和功能。有关详细信息,请参见perlop中的“STRING
”
在exec或system中使用间接对象也更安全。这种用法(也适用于system())强制将参数解释为多值列表,即使列表只有一个参数。这样,您就不会受到shell扩展通配符或拆分带有空格的单词的影响
第一个版本,即没有间接对象的版本,运行echo程序,向它传递一个“惊奇”参数。第二个版本没有;它试图运行一个名为“echo惊喜”的程序,但没有找到它,并设置$?设置为表示故障的非零值
1如何在标记中显示回勾?如中所述:
如果列表中有多个参数,或者列表是数组
使用多个值,启动列表第一个元素给定的程序,并使用列表其余部分给定的参数如果有
只有一个标量参数,将检查该参数是否有shell元字符,如果有,则将整个参数传递给系统的
用于解析的命令shell(在Unix平台上为“/bin/sh-c”,但在其他平台上有所不同)。如果中没有shell元字符
参数时,它被拆分为单词并直接传递给“execvp”,这样更有效
我喜欢使用的system()的版本,它提供了更多的控制,例如能够捕获各种异常并将某些错误代码处理为“坏”,而将其他错误代码处理为“好”:
…这一点在perldoc-f system
中有明确的记录:您的意思是将$filename
放在单引号中吗?我认为如果您确实想调用shell(例如,如果您想要一些管道),所需的引号是$command\u line=“\“$command\”\$arg1\“$arg2\”
@mobrule:我确实打算使用单引号-我正在证明$filename
不会被Perl或Shell扩展…在我的测试脚本中,我使用了“my.$file
”,这给了我一个名为$
的文件-正如我所想的那样。@Jefromi:围绕参数的双引号对嵌入的$,没有帮助,在“$(…)”等符号上打钩。您几乎需要在内容周围使用单引号,但随后需要将嵌入的单引号重写为“”\''
”,这将生成一个单引号以终止当前单引号参数,一个反斜杠引号组合以表示单引号,以及另一个单引号以恢复单引号参数。
$shell = '/bin/csh';
exec $shell '-sh'; # pretend it's a login shell
exec {'/bin/csh'} '-sh'; # pretend it's a login shell
@args = ( "echo surprise" );
exec @args; # subject to shell escapes
# if @args == 1
exec { $args[0] } @args; # safe even with one-arg list
use IPC::System::Simple qw(system);
system("cat *.txt"); # will die on failure