How to pass the contents of a variable as STDIN in qx {} expression in Perl?

Basically I would like to do this:

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

There are two problems here. First, the contents of $_ must be escaped, as it may contain binary data. Secondly, calling echo may be a bit inefficient.

How can I just pass some content as STDIN to a command in Perl?

+3
source share
3 answers

This answer is very naive. It is subject to a deadlock. Do not use it!

ikegami explains in the comment below:

If the parent writes enough to the tube attached to the child STDIN, and if the child outputs enough to the channel attached to its STDOUT, before it starts reading it from STDIN, a deadlock will occur. (On some systems, this may be only 4 KB.) The solution involved using something like selections, streams, etc. The best 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 cases.

I will leave the original answer, but I recommend that readers choose a solution from one of the other answers.


You can use open2 from IPC :: Open2 to read and write to the same process.

Now you do not need to take care of everything.

 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; 

It will open 48 .

Note that you cannot use double quotation marks "" for the exact input that you showed, because the number of backslashes \ is incorrect.

See perldoc open and perlipc for more information.

+3
source

It is further assumed that @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 do not want to record 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 do not want to record the output, but you also do not want to see it, you can use any of the following actions:

 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:

  • Solutions using printf impose a limit on the size of the data passed to the STDIN program.

  • Solutions using printf cannot pass NUL to the STDIN program.

  • The solutions presented using IPC :: Run3 and IPC :: Run do not include a shell. This avoids problems.

  • You should probably use system and capture from IPC :: System :: Simple instead of the built-in system and qx to get a “free” error check.

+4
source

I like the solution provided by @simbabque, as it allows you to call Shell. Anyway, for comparison, a shorter solution can be obtained using Bash (but avoiding echo ) using the 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 <<< '$_'"; 

And, if you need to capture the output of the command:

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

Source: https://habr.com/ru/post/1011581/


All Articles