如何在Perl的qx {}语句中将变量的内容作为STDIN传递?

时间:2016-10-21 09:25:51

标签: perl

我基本上想这样做:

$_ = "some content that need to be escaped &>|\"$\'`\s\\";
qx{echo $_ | foo}

这里有两个问题。首先,$_的内容需要转义,因为它可以包含二进制数据。其次,调用echo可能效率不高。

如何简单地将一些内容作为STDIN传递给Perl中的命令?

3 个答案:

答案 0 :(得分:4)

以下假设@cmd包含程序及其参数(如果有)。

my @cmd = ('foo');

如果要捕获输出,可以使用以下任何一种方法:

use String::ShellQuote qw( shell_quote );
my $cmd1 = shell_quote('printf', '%s', $_);
my $cmd2 = shell_quote(@cmd);
my $output = qx{$cmd1 | $cmd2};

use IPC::Run3 qw( run3 );
run3(\@cmd, \$_, \my $output);

use IPC::Run qw( run );
run(\@cmd, \$_, \my $output);

如果您不想捕获输出,可以使用以下任何一种方法:

use String::ShellQuote qw( shell_quote );
my $cmd1 = shell_quote('printf', '%s', $_);
my $cmd2 = shell_quote(@cmd);
system("$cmd1 | $cmd2");

system('/bin/sh', '-c', 'printf "%s" "$0" | "$@"', $_, @cmd);

use String::ShellQuote qw( shell_quote );
my $cmd = shell_quote(@cmd);
open(my $pipe, '|-', $cmd);
print($pipe $_);
close($pipe);

open(my $pipe, '|-', '/bin/sh', '-c', '"$@"', 'dummy', @cmd);
print($pipe $_);
close($pipe);

use IPC::Run3 qw( run3 );
run3(\@cmd, \$_);

use IPC::Run qw( run );
run(\@cmd, \$_);

如果您不想捕获输出,但又不想看到它,则可以使用以下任何一种方法:

use String::ShellQuote qw( shell_quote );
my $cmd1 = shell_quote('printf', '%s', $_);
my $cmd2 = shell_quote(@cmd);
system("$cmd1 | $cmd2 >/dev/null");

system('/bin/sh', '-c', 'printf "%s" "$0" | "$@" >/dev/null', $_, @cmd);

use String::ShellQuote qw( shell_quote );
my $cmd = shell_quote(@cmd);
open(my $pipe, '|-', "$cmd >/dev/null");
print($pipe $_);
close($pipe);

open(my $pipe, '|-', '/bin/sh', '-c', '"$@" >/dev/null', 'dummy', @cmd);
print($pipe $_);
close($pipe);

use IPC::Run3 qw( run3 );
run3(\@cmd, \$_, \undef);

use IPC::Run qw( run );
run(\@cmd, \$_, \undef);

注意:

  • 使用printf的解决方案会对要传递给程序STDIN的数据大小施加限制。

  • 使用printf的解决方案无法将NUL传递给程序的STDIN。

  • 使用IPC :: Run3和IPC :: Run的解决方案不涉及shell。这可以避免问题。

  • 您应该使用IPC :: System :: Simple中的systemcapture而不是内置systemqx来获取“免费”错误检查。

答案 1 :(得分:3)

这个答案是一种非常天真的方法。它容易陷入僵局。 不要使用它!

ikegami explains in a comment下面:

  

如果父级对附加到子级STDIN的管道写入足够多,并且子级在从STDIN读取之前输出到连接到其STDOUT的管道足够,则会出现死锁。 (在某些系统上,这可能只有4KB。)解决方案涉及使用select,threads等等。更好的解决方案是使用已经为您解决问题的工具(IPC :: Run3或IPC ::跑)。在大多数情况下,IPC :: Open2和IPC :: Open3太低级别无法使用

我将保留原始答案,但鼓励读者从其他答案中选择解决方案。

您可以使用IPC::Open2中的open2来读取和写入相同的流程。

现在你不需要关心逃避任何事情了。

use IPC::Open2;
use FileHandle;

my $writer = FileHandle->new;
my $reader = FileHandle->new;

my $pid = open2( $reader, $writer, 'wc -c' );

# write to the pipe
print $writer 'some content that need to be escaped &>|\"$\'`\s\\';

# tell it you're done
$writer->close;

# read the out of the pipe
my $line = <$reader>;
print $line;

这将打印48

请注意,您不能对您显示的确切输入使用​​双引号"",因为反斜杠的数量\是错误的。

有关详细信息,请参阅perldoc openperlipc

答案 2 :(得分:2)

我喜欢@simbabque提供的解决方案,因为它避免调用Shell。无论如何,为了进行比较,可以使用Bash echo使用Bash(但避免Here string)获得更短的解决方案:

$_ = q{some content that need to be escaped &>|\"$\'`\s\\};
$_ =~ s/'/'"'"'/g; # Bash needs single quotes to be escaped
system 'bash', '-c', "foo <<< '$_'";

并且,如果您需要捕获命令的输出:

use Capture::Tiny 'capture_stdout';
my $res = capture_stdout { system 'bash', '-c', "foo <<< '$_'" };