Perl 何时是使用反勾号的正确时间(和错误时间)?

Perl 何时是使用反勾号的正确时间(和错误时间)?,perl,Perl,许多初级程序员编写的代码如下: sub copy_file ($$) { my $from = shift; my $to = shift; `cp $from $to`; } 这很糟糕,为什么?是否应该使用反勾号?如果是,如何使用?当且仅当您需要捕获命令的输出时,才应使用反勾号。否则,应使用system()。当然,如果有一个Perl函数或CPAN模块来完成这项工作,那么就应该使用它,而不是任何一个 在任何一种情况下,都强烈鼓励做两件事: 首先,清理所有输入:如果代码暴露于可能不

许多初级程序员编写的代码如下:

sub copy_file ($$) {
  my $from = shift;
  my $to = shift;

  `cp $from $to`;
}

这很糟糕,为什么?是否应该使用反勾号?如果是,如何使用?

当且仅当您需要捕获命令的输出时,才应使用反勾号。否则,应使用system()。当然,如果有一个Perl函数或CPAN模块来完成这项工作,那么就应该使用它,而不是任何一个

在任何一种情况下,都强烈鼓励做两件事:

首先,清理所有输入:如果代码暴露于可能不受信任的输入,请使用污染模式(-T)。即使不是,也要确保处理(或防止)诸如空格或三种引号之类的时髦字符

其次,检查返回代码以确保命令成功。下面是一个如何执行此操作的示例:

my $cmd = "./do_something.sh foo bar";
my $output = `$cmd`;

if ($?) {
   die "Error running [$cmd]";
}

您的示例很糟糕,因为有一些perl内置函数可以实现这一点,它们是可移植的,并且通常比backtick选项更有效


只有在没有Perl内置(或模块)替代品的情况下才应该使用它们。这适用于backticks和system()调用。Backticks用于捕获已执行命令的输出。

如果要从命令收集输出,请使用Backticks


否则,
system()
是更好的选择,特别是当您不需要调用shell来处理元字符或命令解析时。您可以通过将列表传递给system(),例如
system('cp','foo','bar')
(不过,对于这个特定的示例,您最好使用一个模块:)

在Perl中,总是有多种方法可以做任何事情。backticks的主要目的是将shell命令的标准输出获取到Perl变量中。(在您的示例中,cp命令打印的任何内容都将返回给调用方。)在您的示例中使用backticks的缺点是您不检查shell命令的返回值;cp可能会失败,你不会注意到。您可以将其与特殊的Perl变量$?一起使用?。当我想要执行shell命令时,我倾向于使用系统

system("cp $from $to") == 0
    or die "Unable to copy $from to $to!";
(还要注意,对于带有嵌入空格的文件名,这将失败,但我认为这不是问题的关键。)

下面是一个人为的例子,说明反勾号在哪里可能有用:

my $user = `whoami`;
chomp $user;
print "Hello, $user!\n";
对于更复杂的情况,您还可以将打开用作管道:

open WHO, "who|"
    or die "who failed";
while(<WHO>) {
    # Do something with each line
}
close WHO;
打开WHO,“WHO |”
或者死“谁失败了”;
while(){
#对每一行做点什么
}
关闭世卫组织;

反勾号只应在您想要捕获输出时使用。在这里使用它们“看起来很愚蠢”。这会让任何看代码的人意识到您对Perl不是很熟悉

如果要捕获输出,请使用反勾号。 如果要运行命令,请使用系统。您将获得的一个优势是能够检查退货状态。
为了便于携带,尽可能使用模块。在这种情况下,File::Copy符合要求。

规则很简单:如果您可以找到一个内置模块来完成相同的工作,或者如果它们是CPAN上的一个健壮模块,可以为您完成这项工作,请不要使用反勾号。backtick通常依赖于不可移植的代码,即使您解开了变量,您仍然可以打开很多安全漏洞


永远不要对用户数据使用反勾号,除非您已经非常严格地指定了允许的内容(而不是不允许的内容——您将错过一些东西)!这是非常非常危险的。

一般来说,最好使用系统而不是反勾号,因为:

  • 系统鼓励调用方检查命令的返回代码

  • 系统允许使用“间接对象”表示法,这更安全,增加了灵活性

  • 背景标记在文化上与shell脚本相关联,这在代码读者中可能并不常见

  • Backticks对可能很繁重的命令使用最小语法

  • 用户可能临时使用backticks而不是system的一个原因是对用户隐藏标准输出。通过重定向STDOUT流,可以更容易、更灵活地实现这一点:

    my $cmd = 'command > /dev/null';
    system($cmd) == 0 or die "system $cmd failed: $?"
    
    此外,摆脱STDERR很容易:

    my $cmd = 'command 2> error_file.txt > /dev/null';
    
    在使用backticks有意义的情况下,我更喜欢使用qx{}来强调出现了一个重载命令

    另一方面,用另一种方法做这件事真的很有帮助。有时您只需要查看命令打印到标准输出的内容。Backticks,当在shell脚本中使用时,正是该作业的合适工具。

    来自“perlop”手册页:

    这并不意味着你应该离开 你的方式,以避免回勾时 他们是得到东西的正确途径 完成。Perl被制成胶水 语言,和它的一个东西 粘在一起就是命令。只是 明白你得到了什么 你自己去吧


    捕获标准输出的另一种方法(除了pid和退出代码外)是使用可能否定的系统和反勾号。

    对于您正在显示的情况,使用模块可能是最好的。然而,为了回答您的问题,每当我需要运行我通常依赖的系统命令时。它提供了许多功能,如收集返回代码、标准输出和错误输出。

    无论您做什么,以及清理输入和检查代码的返回值,请确保您使用显式完整路径调用任何外部程序。e、 g.说

    my $user = `/bin/whoami`;
    


    如果用户的路径发生变化,只说“whoami”或“cp”会有意外运行您想要的命令的风险,这是一个恶意攻击者可能试图利用的安全漏洞。

    Perl具有分裂的个性。一方面,它是一种伟大的脚本语言,可以取代shell的使用。在这种一次性观察结果的使用中,回击是很方便的。 当使用程序时
    my $result = `/bin/cp $from $to`;
    
    #!/usr/bin/perl -w
    use strict;
    use IPC::System::Simple qw(capture);
    
    # Make sure we're called with command-line arguments.
    @ARGV or die "Usage: $0 arguments\n";
    
    my $documentation = capture('perldoc', @ARGV);
    
    
    sub run_cmd {
      my $cmd = shift @_;
      my @args = @_;
    
      my $fh; # file handle
      my $pid = open($fh, '-|');
      defined($pid) or die "Could not fork";
      if ($pid == 0) {
        open STDERR, '>/dev/null';
        # setuid() if necessary
        exec ($cmd, @args) or exit 1;
      }
      wait; # may want to time out here?
      if ($? >> 8) { die "Error running $cmd: [$?]"; }
      while (<$fh>) {
        # Have fun with the output of $cmd
      }
      close $fh;
    }