Perl 使用 bash 反引号

问题描述 投票:0回答:7

在 Perl 中,执行反引号的默认 shell 是 sh。我想改用 bash,因为它的语法更丰富。到目前为止我发现建议的解决方案是

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

明显的缺点是转义的双引号,这意味着我将很难在 bash 参数中使用双引号。例如,如果我想在 bash 中运行需要双引号的命令

echo "a'b"

上面的方法会很别扭。

Perl 的 system() 调用对此问题有一个解决方案:使用 ARRAY args,

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

这使我原来的 bash 命令几乎始终保持不变。

我也想在反引号中使用 ARRAY args。可以吗?

bash perl backticks
7个回答
4
投票

一个,一个可以提交一份清单给

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(...)
运算符形式来演示另一种方式(对于包含单引号也很有用);对于这个简单的例子来说没有必要。还是要注意细节;这是使用复杂外部命令的本质,任何方法或工具都无法完全避免。

至于运行

bash
,请注意,通常
sh
委托给系统上的默认 shell,并且在许多系统上使用的是
bash
。但如果您没有,在命令中使用
bash -c
的一种方法是首先准备命令,然后将其添加到
qx
字符串

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 字符串,可能还需要做一些工作,但其他一切都会更好。选项比比皆是。例如

  • 为什么要使用 Perl(远远)丰富的外部命令?它是一种完整的、非常完整的编程语言,而不是具有一些编程功能的命令解释器。如果您需要 shell 的功能,为什么不通过 shell 只运行这些东西并在 Perl 中执行其他所有操作呢?


2
投票

Capture::Tiny 是一个非常好的选择:正如概要所示,你可以这样做

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

以及使用内部系统

capture_stdout
如果您想要更简单的反引号行为。

此外,它非常通用,可以处理 Perl 代码(甚至是执行奇怪操作的 Perl 代码)以及外部程序,因此在您的工具箱中拥有它是一件好事。


2
投票

我有以下可用的子程序

    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() 调用的方式一样,我仍然希望如此。


2
投票

给定

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;

0
投票

您可以更改 perl 使用的 shell :

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

0
投票

您可以使用单引号作为分隔符与

qx
,如下所示:

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

这将根据 perlop 保护命令免受 Perl 的双引号插值的影响。

不幸的是,这不适用于单引号。例如,如果您想做

echo "'"
,您需要以下内容:

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

编辑

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

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

0
投票

如果安全等方面允许的话,对整个脚本使用临时文件怎么样?

例如,我需要使用以下 Perl 代码的过滤器脚本和数据来运行

jq
。代码是这样的

use File::Temp qw(tempfile);

my $BashCode = "jq -r '$JQCode' <<< '$JSON'";

# create a temp script file
my ($fh, $filename) = tempfile();
print $fh $BashCode;
close $fh;

# run the script
$out = `bash $filename`;

如您所知,当此命令行与

<<<
(带反引号)一起运行时,
sh
会导致错误。而且
bash -c
在这种情况下并不容易起作用。所以,这就是解决方案。

您可以切换使用 bash,因为它的语法更丰富,无需额外转义。

© www.soinside.com 2019 - 2024. All rights reserved.