使用bash的Perl反勾

rkue9o1l  于 2022-11-15  发布在  Perl
关注(0)|答案(6)|浏览(180)

在Perl中,执行反勾的默认shell是sh。

`bash -c \"echo a b\"`

明显的缺点是转义的双引号,这意味着我将很难在bash参数中使用双引号。

echo "a'b"

以上方法会很别扭。
Perl的system()调用有一个解决这个问题的方法:要使用ARRAY参数,

system("bash", "-c", qq(echo "a'b"));

这使我的原始bash命令保持不变,而且几乎总是如此。
我也想在反勾号中使用ARRAY参数,可以吗?

i86rm4rw

i86rm4rw1#

对于一个,一个 * 可以 * 提交一个列表到qx;它被插入到一个字符串中,然后传递给execvp或shell(参见qx,以及this post的第二部分和注解)。如果您 * 需要 * 一个shell,那么假定该字符串包含shell元字符,因此它将通过shell传递。

my @cmd = qw(ls -l "dir with spaces");
#my @cmd = qw(ls -l "dir with spaces" > outfile);
my @out = qx(@cmd);
print for @out;

我创建了一个"dir with spaces"目录,其中包含一个要测试的文件(对于带引号的命令,需要使用shell)。
接下来,原则上我会推荐一个模块来编写这些shell命令,而不是像String::ShellQuote那样通过一个令人头痛的过程来正确地转义并传递所有命令

use String::ShellQuote qw(shell_quote); 

my @cmd = ('ls', '-l', q(dir with spaces)); 

my $quoted = shell_quote(@cmd);; 
my @out = qx($quoted); 
#my @out = qx($quoted > outfile); 
print for @out;

我使用q(...)运算符形式的单引号来演示另一种方法(包含单引号也很有用);对于这个简单的例子来说,这是不必要的。2人们仍然需要注意细节;这是使用复杂外部命令的本质,任何方法或工具都 * 无法 * 完全避免。
至于运行bash,请注意,通常情况下,sh会委托给系统上的默认shell,并且在许多系统上使用的是bash

my @cmd = ('ls', '-l', q(dir with spaces)); 
my $quoted = shell_quote(@cmd); 
my @out = qx(bash -c "$quoted"); 
#my @out = qx(bash -c "$quoted" > outfile); 
print for @out;

我想再提几点意见:

  • qx是一个古老的恶魔。使用现代的工具/模块运行外部命令怎么样?为了准备你所涉及的bash字符串,可能需要做更多的工作,但其他一切都会更好。选项很多。例如
  • IPC::System::Simple及其少量实用程序函数
  • 使用Capture::Tiny以您喜欢的语法 Package system调用
  • IPC::Run可以完成所有这些任务,而且还可以完成更多任务
  • 为什么要使用外部命令呢?Perl上级丰富性远远超过了外部命令。它是一种完整的、非常完整的编程语言,而命令解释器只具有一些编程功能。如果你需要shell的功能,为什么不通过shell运行这些功能,而用Perl来做其他所有的事情呢?
pbpqsu0x

pbpqsu0x2#

Capture::Tiny是一个非常好的选项:如概要所示,您可以

use Capture::Tiny 'capture';
my ($output, $error_output, $exit_code) = capture {
    system(@whatever);
};

如果您需要更简单的反勾行为,也可以使用systeminnercapture_stdout
另外,它是非常通用的,可以处理Perl代码(甚至是做奇怪事情的Perl代码)以及外部程序,所以在您的工具箱中有它是一件好事。

yzckvree

yzckvree3#

我有下面的潜艇

sub bash_output {
       my ($cmd) = @_; 
    
       open my $ifh, "-|", "bash", "-c", $cmd or die "cannot open file handler: $!";
    
       my $output = ""; 
       while (<$ifh>) {
          $output .= $_; 
       }   
    
       close $ifh;
    
       return $output;
    }

    print "test bash_output()\n";

    my @strings = (
         qq(echo "a'b"),
         'echo $BASH_VERSION',
         '[[ "abcde" =~ bcd ]] && echo matched',
         'i=1; ((i++)); echo $i',
   );

   for my $s (@strings) {
         print "bash_output($s) = ", bash_output($s), "\n";
   }

输出为

bash_output(echo "a'b") = a'b

bash_output(echo $BASH_VERSION) = 4.4.20(1)-release

bash_output([[ "abcde" =~ bcd ]] && echo matched) = matched

bash_output(i=1; ((i++)); echo $i) = 2

我的回答是冗长的,但它满足了我的需要。我希望Perl有一个内置的解决方案,就像它如何处理system()调用一样,我仍然希望。

8ljdwjyq

8ljdwjyq4#

给定

my @cmd = ( "bash", "-c", qq(echo "a'b") );

您可以使用下列任一项:

use Shell::Quote qw( shell_quote );

my $cmd = shell_quote( @cmd );
my $output = `$cmd`;
die "Can't spawn child: $!\n"                    if $? == -1;
die "Child killed by signal ".( $? & 0x7F )."\n" if $? & 0x7F;
die "Child exited with error ".( $? >> 8 )."\n"  if $? >> 8;

use IPC::System::Simple qw( capturex );

my $output = capturex( @cmd );

use IPC::Run qw( run );

run \@cmd, '>', \my $output;
die "Child killed by signal ".( $? & 0x7F )."\n" if $? & 0x7F;
die "Child exited with error ".( $? >> 8 )."\n"  if $? >> 8;
xwmevbvl

xwmevbvl5#

您可以更改perl使用的shell:

$ENV{PERL5SHELL} = "bash";
my $out = qx{echo "Hello ' world from bash \$BASH_VERSION"};
print($out);
64jmpszr

64jmpszr6#

您可以使用单引号作为qx的分隔符,如下所示:

my $out = qx'bash -c "echo a b"';

这将根据perlop保护命令不受Perl的双引号插入的影响。
不幸的是,这对单引号不起作用。例如,如果你想执行echo "'",你需要以下代码:

my $out = `bash -c \"echo \\\"'\\\"\"`;

编辑

为了帮助您管理引号的转义,您可以使用如下的helper函数:

use experimental qw(signatures);
sub bash_backticks($code) {
    $code =~ s/'/'"'"'/g;
    `bash -c '$code'`
}

相关问题