如何使用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