How to use both pipes and prevent shell expansion in perl system function?

时光毁灭记忆、已成空白 提交于 2019-12-02 14:12:41

system has three calling conventions:

system($SHELL_CMD)

system($PROG, @ARGS)               # @ARGS>0

system( { $PROG } $NAME, @ARGS )   # @ARGS>=0

The first passes a command to the shell. It's equivalent to

system('/bin/sh', '-c', $SHELL_CMD)

The other two execute the program $PROG. system never prevents shell expansion or performs any escaping. There's simply no shell involved.

So your question is about building a shell command. If you were at the prompt, you might use

echo \* | cat -n

or

echo '*' | cat -n

to pass *. You need a function that performs the job of escaping * before interpolating it. Fortunately, one already exists: String::ShellQuote's shell_quote.

$ perl -e'
   use String::ShellQuote qw( shell_quote );
   my $s = "*";
   my $cmd1 = shell_quote("printf", q{%s\n}, $s);
   my $cmd2 = "cat -n";
   my $cmd = "$cmd1 | $cmd2";
   print("Executing <<$cmd>>\n");
   system($cmd);
'
Executing <<printf '%s\n' '*' | cat -n>>
     1  *

I used printf instead of echo since it's very hard to handle arguments starting with - in echo. Most programs accept -- to separate options from non-options, but not my echo.

All these complications beg the question: Why are you shelling out to send an email? It's usually much harder to handle errors from external programs than from libraries.

You can use open to pipe directly to mailx, without your content being interpreted by the shell:

open( my $mail, "|-", "mailx", "-s", $email_subject, $recipient );
say $mail $email_message;
close $mail;

More details can be found in open section of perlipc.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!