使用 bash 的 Perl 反引号
Perl backticks using bash
在 Perl 中,执行反引号的默认 shell 是 sh。我想改用 bash 以获得更丰富的语法。到目前为止,我发现建议的解决方案是
`bash -c \"echo a b\"`
明显的缺点是转义双引号,这意味着我将很难在 bash args 中使用双引号。例如,如果我想 运行 命令在 bash
中需要双引号
echo "a'b"
上面的方法会很别扭
Perl 的 system() 调用有一个解决这个问题的方法:使用 ARRAY args,
system("bash", "-c", qq(echo "a'b"));
这使我原来的 bash 命令保持不变,而且几乎总是如此。
我也想在反引号中使用 ARRAY args。可能吗?
您可以像这样在 qx
中使用单引号作为分隔符:
my $out = qx'bash -c "echo a b"';
这将根据 perlop 保护命令免受 Perl 的 double-quote 插值。
不幸的是,这对单引号不起作用。例如,如果您想执行 echo "'"
,则需要以下内容:
my $out = `bash -c \"echo \\"'\\"\"`;
编辑:
为了帮助您管理引号的转义,您可以使用这样的辅助函数:
use experimental qw(signatures);
sub bash_backticks($code) {
$code =~ s/'/'"'"'/g;
`bash -c '$code'`
}
您可以更改 perl 使用的 shell :
$ENV{PERL5SHELL} = "bash";
my $out = qx{echo "Hello ' world from bash $BASH_VERSION"};
print($out);
我有以下可用的子
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
我的答案是long-winded,但它满足了我的需要。我希望 Perl 有一个 built-in 解决方案,就像它处理 system() 调用的方式一样,我仍然希望如此。
一个,一个可以提交一个列表给qx
;它被插入到一个字符串中,然后传递给 execvp
或 shell(参见 qx, and the second part of 和注释)。如果你 需要 一个 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 命令,而不是通过 nail-biter 来正确转义并传递所有命令,例如 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(...)
运算符形式来演示另一种方式(对于包含单引号也很有用);这个简单的例子没有必要。人们仍然需要注意细节;这是使用复杂外部命令的本质,无法通过任何方法或工具完全避免。
至于 运行ning bash
,请注意通常 sh
在您的系统上委托给 default-of-sorts 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
是上古恶魔。将现代 tools/modules 用于 运行ning 外部命令怎么样?为了准备您所涉及的 bash 字符串,可能还有一些工作要做,但其他一切都会更好。选择比比皆是。例如
IPC::System::Simple 及其一些实用功能
使用 Capture::Tiny 以您喜欢的语法包装 system
调用
IPC::Run 可以做所有这一切,然后做得更多
为什么要使用外部命令,而 Perl 的(远远)更丰富?它是一种完整的、非常完整的编程语言,与具有一些编程功能的 command-interpreter 相比。如果你需要 shell 的功能,为什么不 运行 通过 shell 做那些事情,而在 Perl 中做所有其他事情?
Capture::Tiny 是一个很好的选择:如 SYNOPSIS 所示,您可以这样做
use Capture::Tiny 'capture';
my ($output, $error_output, $exit_code) = capture {
system(@whatever);
};
如果您想要更简单的反引号行为,也可以在 capture_stdout
中使用系统。
此外,它非常 general-purpose,可以处理 Perl 代码(甚至是做奇怪事情的 Perl 代码)以及外部程序,所以在您的工具箱中拥有它是一件好事。
给出
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;
在 Perl 中,执行反引号的默认 shell 是 sh。我想改用 bash 以获得更丰富的语法。到目前为止,我发现建议的解决方案是
`bash -c \"echo a b\"`
明显的缺点是转义双引号,这意味着我将很难在 bash args 中使用双引号。例如,如果我想 运行 命令在 bash
中需要双引号echo "a'b"
上面的方法会很别扭
Perl 的 system() 调用有一个解决这个问题的方法:使用 ARRAY args,
system("bash", "-c", qq(echo "a'b"));
这使我原来的 bash 命令保持不变,而且几乎总是如此。
我也想在反引号中使用 ARRAY args。可能吗?
您可以像这样在 qx
中使用单引号作为分隔符:
my $out = qx'bash -c "echo a b"';
这将根据 perlop 保护命令免受 Perl 的 double-quote 插值。
不幸的是,这对单引号不起作用。例如,如果您想执行 echo "'"
,则需要以下内容:
my $out = `bash -c \"echo \\"'\\"\"`;
编辑:
为了帮助您管理引号的转义,您可以使用这样的辅助函数:
use experimental qw(signatures);
sub bash_backticks($code) {
$code =~ s/'/'"'"'/g;
`bash -c '$code'`
}
您可以更改 perl 使用的 shell :
$ENV{PERL5SHELL} = "bash";
my $out = qx{echo "Hello ' world from bash $BASH_VERSION"};
print($out);
我有以下可用的子
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
我的答案是long-winded,但它满足了我的需要。我希望 Perl 有一个 built-in 解决方案,就像它处理 system() 调用的方式一样,我仍然希望如此。
一个,一个可以提交一个列表给qx
;它被插入到一个字符串中,然后传递给 execvp
或 shell(参见 qx, and the second part of
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 命令,而不是通过 nail-biter 来正确转义并传递所有命令,例如 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(...)
运算符形式来演示另一种方式(对于包含单引号也很有用);这个简单的例子没有必要。人们仍然需要注意细节;这是使用复杂外部命令的本质,无法通过任何方法或工具完全避免。
至于 运行ning bash
,请注意通常 sh
在您的系统上委托给 default-of-sorts 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
是上古恶魔。将现代 tools/modules 用于 运行ning 外部命令怎么样?为了准备您所涉及的 bash 字符串,可能还有一些工作要做,但其他一切都会更好。选择比比皆是。例如IPC::System::Simple 及其一些实用功能
使用 Capture::Tiny 以您喜欢的语法包装
system
调用IPC::Run 可以做所有这一切,然后做得更多
为什么要使用外部命令,而 Perl 的(远远)更丰富?它是一种完整的、非常完整的编程语言,与具有一些编程功能的 command-interpreter 相比。如果你需要 shell 的功能,为什么不 运行 通过 shell 做那些事情,而在 Perl 中做所有其他事情?
Capture::Tiny 是一个很好的选择:如 SYNOPSIS 所示,您可以这样做
use Capture::Tiny 'capture';
my ($output, $error_output, $exit_code) = capture {
system(@whatever);
};
如果您想要更简单的反引号行为,也可以在 capture_stdout
中使用系统。
此外,它非常 general-purpose,可以处理 Perl 代码(甚至是做奇怪事情的 Perl 代码)以及外部程序,所以在您的工具箱中拥有它是一件好事。
给出
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;