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

now*_*wox 4 perl

我基本上想这样做:

$_ = "some content that need to be escaped &>|\"$\'`\s\\";
qx{echo $_ | foo}
Run Code Online (Sandbox Code Playgroud)

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

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

ike*_*ami 5

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

my @cmd = ('foo');
Run Code Online (Sandbox Code Playgroud)

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

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

use IPC::Run3 qw( run3 );
run3(\@cmd, \$_, \my $output);
Run Code Online (Sandbox Code Playgroud)

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

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

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

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

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

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

use IPC::Run3 qw( run3 );
run3(\@cmd, \$_);
Run Code Online (Sandbox Code Playgroud)

use IPC::Run qw( run );
run(\@cmd, \$_);
Run Code Online (Sandbox Code Playgroud)

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

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

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

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

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

use IPC::Run3 qw( run3 );
run3(\@cmd, \$_, \undef);
Run Code Online (Sandbox Code Playgroud)

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

笔记:

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

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

  • 所提出的使用 IPC::Run3 和 IPC::Run 的解决方案不涉及外壳。这避免了问题。

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


sim*_*que 3

这个答案是一种非常幼稚的做法。很容易陷入僵局。不要使用它!

ikegami 在下面的评论中解释道

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

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


您可以使用IPC::Open2open2来读取和写入同一进程。

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

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;
Run Code Online (Sandbox Code Playgroud)

这将打印48.

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

有关详细信息,请参阅perldocopenperlipc 。

  • @nowox 根据我的“corelist”,IPC::Open2 和 FileHandle 都已_首次与 perl 5 一起发布。我相信这意味着自从 Perl 5 最初发布以来。它没有比这更多的核心模块了。:) (5认同)
  • 顺便说一句,File::Temp 是在 5.6.1 中附带的。 (3认同)
  • 如果父级向附加到子级 STDIN 的管道写入了足够的数据,并且子级在从其 STDIN 读取之前向附加到其 STDOUT 的管道输出了足够多的数据,则会出现死锁。(在某些系统上这可能只有 4KB。)解决方案涉及使用“select”、线程等。更好的解决方案是使用已经为您解决问题的工具(IPC::Run3 或 IPC ::跑步)。IPC::Open2 和 IPC::Open3 级别太低,在大多数情况下没有用处。 (3认同)