The Perl manual describes a totally devious construct that will work under any of csh, sh, or Perl, such as the following:
eval '(exit $?0)' && eval 'exec perl -wS $0 ${1+"$@"}'
& eval 'exec /usr/bin/perl -wS $0 $argv:q'
if $running_under_some_shell;
Devious indeed... can someone please explain in detail how this works?
The idea is that those three lines do 3 different things if they're evaluated in a standard Bourne shell (sh), a C shell (csh), or Perl. This hack is only needed on systems that don't support specifying an interpreter name using a
#!
line at the start of a script. If you execute a Perl script beginning with those 3 lines as a shell script, the shell will launch the Perl interpreter, passing it the script's filename and the command line arguments.In Perl, the three lines form one statement, terminated by the
;
, of the formSince the script just started,
$running_under_some_shell
isundef
, which is false, and the evals are never executed. It's a no-op.The devious part is that
$?0
is parsed differently in sh versus csh. In sh, that means$?
(the exit status of the last command) followed by 0. Since there is no previous command,$?
will be 0, so$?0
evaluates to00
. In csh,$?0
is a special variable that is 1 if the current input filename is known, or 0 if it isn't. Since the shell is reading these lines from a script,$?0
will be 1.Therefore, in sh,
eval '(exit $?0)'
meanseval '(exit 00)'
, and in csh it meanseval '(exit 1)'
. The parens indicate that the exit command should be evaluated in a subshell.Both sh and csh understand
&&
to mean "execute the previous command, then execute the following command only if the previous command exited 0". So only sh will executeeval 'exec perl -wS $0 ${1+"$@"}'
. csh will proceed to the next line.csh will ignore "& " at the beginning of a line. (I'm not sure exactly what that means to csh. Its purpose is to make this a single expression from Perl's point of view.) csh then proceeds to evaluate
eval 'exec /usr/bin/perl -wS $0 $argv:q'
.These two command lines are quite similar.
exec perl
means to replace the current process by launching a copy ofperl
.-wS
means the same as-w
(enable warnings) and-S
(look for the specified script in$PATH
).$0
is the filename of the script. Finally both${1+"$@"}
and$argv:q
produce a copy of the current command line arguments (in sh and csh, respectively).It uses
${1+"$@"}
instead of the more usual"$@"
to work around a bug in some ancient version of the Bourne shell. They mean the same thing. You can read the details in Bennett Todd's explanation (copied in gbacon's answer).From Tom Christiansen's collection Far More Than Everything You've Ever Wanted to Know About …:
Why we use
eval 'exec perl $0 -S ${1+"$@"}'