The other day, on the heels of a brief exchange of tweets:
Make a Perl program print itself:
seek(DATA,0,0); print while (<DATA>); __DATA__
— PerlTricks (@PerlTricks) September 19, 2014
@PerlTricks @PerlSawyer shorter version:
open 0; print while <0>;
— Rafaël Garcia-Suarez (@consttype) September 19, 2014
@consttype @PerlTricks @PerlSawyer if *short* (as in golfing) is a measurement here,
open 0;print for<0>;
— Tux (H.Merijn Brand) (@Tux5) September 19, 2014
I chirped in to point out that neither the while
nor the for
is necessary as the arguments to print
print are evaluated in list context, therefore automatically slurping the contents of the bareword filehandle 0
, and printing them:
@Tux5 @consttype @PerlTricks @PerlSawyer Actually, if you are golfing,
open 0; print <0>;
will do.
— A. Sinan Unur (@sinan_unur) October 2, 2014
But, why is open 0;
equivalent to opening the source of the script that is running?
You probably do know that the special variable $0
holds the name of the program that is being executed. It is not universally guaranteed (see perldoc -v '$0'
), but this will usually be a string you can pass to open
to open the source of your script.
But, how does open 0
end up opening this file?
To my dismay, I found that perldoc -f open
in most recent versions of Perl don’t have this, but versions as recent as 5.18.2
explain what happens when open
is invoked with a single argument:
If
EXPR
is omitted, the global (package) scalar variable of the same name as theFILEHANDLE
contains the filename.
Clearly, I don’t recommend relying on this: If nothing else, you should avoid using global variables, and you should use the three argument form of open
anyway. I am just explaining how this trick works.
So, when perl
sees open 0;
, it does the equivalent of open 0, '<', $0
.
You can easily verify this:
$ cat 0.pl
#!/usr/bin/env perl
# We are not golfing any more
use autodie;
use strict;
use warnings;
$0 = 'does not exist'; # Thanks Peter
open 0;
$ ./0.pl
Can't open('0'): No such file or directory at ./0.pl line 9
Another example:
$ cat evil.pl
#!/usr/bin/env perl
'does not exist.txt' =~ /\A(.+)\z/;
open 1;
print <1>;
$ cat 'does not exist.txt'
it
does
exist
-- Dr. Evil
$ ./evil.pl
it
does
exist
-- Dr. Evil
Ouch!