I basically would like to do this:
$_ = "some content that need to be escaped &>|\"$\'`\s\\";
qx{echo $_ | foo}
There are two problems here. First the content of $_
needs to be escaped as it can contain binary data. Second, invoking echo
might be slightly inefficient.
How can I simply pipe some content as STDIN to a command in Perl?
The following assume @cmd
contains the program and its arguments (if any).
my @cmd = ('foo');
If you want to capture the output, you can use any of the following:
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);
If you don't want to capture the output, you can use any of the following:
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, \$_);
If you don't want to capture the output, but you don't want to see it either, you can use any of the following:
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);
Notes:
The solutions using printf
will impose a limit on the size of the data to pass to the program's STDIN.
The solutions using printf
are unable to pass a NUL to the program's STDIN.
The presented solutions that use IPC::Run3 and IPC::Run don't involve a shell. This avoids problems.
You should probably use system
and capture
from IPC::System::Simple instead of the builtin system
and qx
to get "free" error checking.
This answer is a very naive approach. It's prone to deadlock. Don't use it!
ikegami explains in a comment below:
If the parent writes enough to the pipe attached to the child's STDIN, and if the child outputs enough to the pipe attached to its STDOUT before it reads from its STDIN, there will be a deadlock. (This can be as little as 4KB on some systems.) The solution involved using something like select, threads, etc. The better solution is to use a tool that has already solved the problem for you (IPC::Run3 or IPC::Run). IPC::Open2 and IPC::Open3 are too low-level to be useful in most circumstances
I'll leave the original answer, but encourage readers to pick the solution from one of the other answers instead.
You can use open2
from IPC::Open2 to read and write to the same process.
Now you don't need to care about escaping anything.
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;
This will print 48
.
Note that you can't use double quotes ""
for the exact input you showed because the number of backslashes \
is wrong.
See perldoc open
and perlipc for more information.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With