Gregory Nisbet Gregory Nisbet - 5 months ago 8
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'