I know this is incorrect. I just want to know how perl parses this.
So, I'm playing around with perl, what I wanted was perl -ne
what I typed was perl -ie
the behavior was kind of interesting, and I'd like to know what happened.
$ echo 1 | perl -ie'next unless /g/i'
So perl Aborted (core dumped)
on that. Reading perl --help
I see -i
takes an extension for backups.
-i[extension] edit <> files in place (makes backup if extension supplied)
For those that don't know -e
is just eval. So I'm thinking one of three things could have happened either it was parsed as
perl -i -e'next unless /g/i'
i gets undef, the rest goes as argument to e
perl -ie 'next unless /g/i'
i gets the argument e, the rest is hanging like a file name
perl -i"-e'next unless /g/i'"
whole thing as an argument to i
When I run
$ echo 1 | perl -i -e'next unless /g/i'
The program doesn't abort. This leads me to believe that 'next unless /g/i'
is not being parsed as a literal argument to -e
. Unambiguously the above would be parsed that way and it has a different result.
So what is it? Well playing around with a little more, I got
$ echo 1 | perl -ie'foo bar'
Unrecognized switch: -bar (-h will show valid options).
$ echo 1 | perl -ie'foo w w w'
... works fine guess it reads it as `perl -ie'foo' -w -w -w`
Playing around with the above, I try this...
$ echo 1 | perl -ie'foo e eval q[warn "bar"]'
bar at (eval 1) line 1.
Now I'm really confused.. So how is Perl parsing this? Lastly, it seems you can actually get a Perl eval command from within just -i
. Does this have security implications?
$ perl -i'foo e eval "warn q[bar]" '
Shell quote-processing is collapsing and concatenating what it thinks is all one argument. Your invocation is equivalent to
$ perl '-ienext unless /g/i'
It aborts immediately because perl parses this argument as containing -u
, which triggers a core dump where execution of your code would begin. This is an old feature that was once used for creating pseudo-executables, but it is vestigial in nature these days.
What appears to be a call to eval
is the misparse of -e 'ss /g/i'
.
B::Deparse can your friend, provided you happen to be running on a system without dump
support.
$ echo 1 | perl -MO=Deparse,-p -ie'next unless /g/i'
dump is not supported.
BEGIN { $^I = "enext"; }
BEGIN { $/ = "\n"; $\ = "\n"; }
LINE: while (defined(($_ = <ARGV>))) {
chomp($_);
(('ss' / 'g') / 'i');
}
So why does unle
disappear? If you’re running Linux, you may not have even gotten as far as I did. The output above is from Perl on Cygwin, and the error about dump
being unsupported is a clue.
Of note from the perlrun documentation:
-u
This switch causes Perl to dump core after compiling your program. You can then in theory take this core dump and turn it into an executable file by using the undump program (not supplied). This speeds startup at the expense of some disk space (which you can minimize by stripping the executable). (Still, a "hello world" executable comes out to about 200K on my machine.) If you want to execute a portion of your program before dumping, use the
dump
operator instead. Note: availability of undump is platform specific and may not be available for a specific port of Perl.
Perl’s argument processing sees the entire chunk as a single cluster of options because it begins with a dash. The -i
option consumes the next word (enext
), as we can see in the implementation for -i
processing.
case 'i':
Safefree(PL_inplace);
[Cygwin-specific code elided -geb]
{
const char * const start = ++s;
while (*s && !isSPACE(*s))
++s;
PL_inplace = savepvn(start, s - start);
}
if (*s) {
++s;
if (*s == '-') /* Additional switches on #! line. */
s++;
}
return s;
For the backup file’s extension, the code above from perl.c consumes up to the first whitespace character or end-of-string, whichever is first. If characters remain, the first must be whitespace, then skip it, and if the next is a dash then skip it also. In Perl, you might write this logic as
if ($$s =~ s/i(\S+)(?:\s-)//) {
my $extension = $1;
return $extension;
}
Then, all of -u
, -n
, -l
, and -e
are valid Perl options, so argument processing eats them and leaves the nonsensical
ss /g/i
as the argument to -e
, which perl parses as a series of divisions. But before execution can even begin, the archaic -u
causes perl to dump core.
An even stranger bit is if you put two spaces between next
and unless
$ perl -ie'next unless /g/i'
the program attempts to run. Back in the main option-processing loop we see
case '*':
case ' ':
while( *s == ' ' )
++s;
if (s[0] == '-') /* Additional switches on #! line. */
return s+1;
break;
The extra space terminates option parsing for that argument. Witness:
$ perl -ie'next nonsense -garbage --foo' -e die Died at -e line 1.
but without the extra space we see
$ perl -ie'next nonsense -garbage --foo' -e die Unrecognized switch: -onsense -garbage --foo (-h will show valid options).
With an extra space and dash, however,
$ perl -ie'next -unless /g/i' dump is not supported.
As the comments indicate, the logic is there for the sake of harsh shebang (#!
) line constraints, which perl does its best to work around.
Interpreter scripts
An interpreter script is a text file that has execute permission enabled and whose first line is of the form:
#! interpreter [optional-arg]
The interpreter must be a valid pathname for an executable which is not itself a script. If the filename argument of
execve
specifies an interpreter script, then interpreter will be invoked with the following arguments:interpreter [optional-arg] filename arg...
where arg... is the series of words pointed to by the
argv
argument ofexecve
.For portable use, optional-arg should either be absent, or be specified as a single word (i.e., it should not contain white space) …
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With