Gregory Nisbet Gregory Nisbet - 9 months ago 23
Perl Question

Perl only executes part of shell command and emits warning 'redundant argument in printf' in command substitution

The following has been tested with perl 5.24 on OS X 10.11.5.

I wrote a short program (

perl-embed.pl
) to determine whether perl escapes shell metacharacters when interpolating strings into backticks (it doesn't).

use strict;
use warnings;

my $bar = '" ; echo 45 ; "';

printf "%s\n", `echo "hi${bar}ls"`;


I was very surprised to see that this generated a warning and only executed part of the command.

$ perl perl-embed.pl
Redundant argument in printf at perl-embed.pl line 6.
hi


For comparison the following program (
perl-embed2.pl
) with
print
instead of
printf
runs without warnings.

use strict;
use warnings;

my $bar = '" ; echo 45 ; "';

print `echo "hi${bar}ls"`;


I then ran it.

$ perl perl-embed2.pl
hi
45
<contents of current working directory>


perl-embed.pl
's behavior is totally unexpected.
printf
interpolates the contents of strings just fine in other contexts, even if the string contains weird characters.

$ perl -Mstrict -Mwarnings -e 'printf "%s\n", q[5]'
5

$ perl -Mstrict -Mwarnings -e 'printf "%s\n", q["]'
"


The system perl (version 5.18) does not emit this warning, but seems not to execute
ls
or echo
45
like we would expect

$ /usr/bin/perl perl-embed.pl
hi

$ /usr/bin/perl perl-embed2.pl
hi
45
<contents of current directory>


Why is perl behaving this way? Note that in every case perl is exiting normally.

mob mob
Answer

You are using backticks in list context, so the expression

`echo "hi${bar}ls`

will run the command

 `echo "hi"; echo 45; ls`

and return each line of output in a separate element, for example

( "hi",
  "45",
  "foo",
  ...     # other files in current directory
 )

But the template in printf ("%s\n") only has one placeholder, so printf gets confused and issues the warning, just as if you said

perl -we 'printf "%d\n", 1, 2, 3, 4'