How to use both pipes and prevent shell expansion in a perl system function?
If multiple arguments are passed to a perl system function, the shell extension won't work:
# COMMAND
$ perl -e 'my $s="*"; system("echo", "$s" )'
# RESULT
*
If the command is passed as one argument, the extension will work:
# COMMAND
$ perl -e 'my $s="echo *"; system("$s")'
# RESULT
Desktop Documents Downloads
The system function also allows multiple commands to be used and connected using pipes. This only works when the argument is passed as one command:
# COMMAND
$ perl -e 'my $s="echo * | cat -n"; system("$s")'
# RESULT
1 Desktop Documents Downloads
How can I combine the above commands and use both pipes and prevent the shell from expanding?
I tried:
# COMMAND
$ perl -e 'my $s="echo"; system("$s", "* | cat -n")'
# RESULT
* | cat -n
but it didn't work due to the reasons I described above (multiple arguments not expanded). As a result, I want:
1 *
EDIT: The problem I am facing is that when I use the following command:
system("echo \"$email_message\" | mailx -s \"$email_subject\" $recipient");
Then $ email_message is decrypted and it will split mailx if it contains some characters that are further expanded by the shell.
system
has three calling conventions:
system($SHELL_CMD)
system($PROG, @ARGS) # @ARGS>0
system( { $PROG } $NAME, @ARGS ) # @ARGS>=0
The first passes the command to the shell. This is equivalent to
system('/bin/sh', '-c', $SHELL_CMD)
The other two execute the program $PROG
. system
never prevents the shell from expanding or does any kind of escaping. There is simply no shell involved.
So your question is about creating a shell command. If you were on the command line, you can use
echo \* | cat -n
or
echo '*' | cat -n
transfer *
. You will need a function to do the acceleration task *
before interpolating it. Fortunately, there is already: String :: ShellQuote 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 echo
as it is very difficult to handle arguments starting with -
in echo
. Most programs accept --
to separate options from non-parameters, but not my echo
.
All of these complications raise the question: why are you shelling to send an email? It is usually much more difficult to handle errors from external programs than from libraries.
You can use open
to connect 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 the open section of perlipc .