开发者

Explain the deviousness of the Perl "preamble"

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/pe开发者_StackOverflow中文版rl -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 form

eval '...' && eval '...' & eval '...' if $running_under_some_shell;

Since the script just started, $running_under_some_shell is undef, 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 to 00. 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)' means eval '(exit 00)', and in csh it means eval '(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 execute eval '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 of perl. -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+"$@"}'

Newsgroups: comp.lang.tcl,comp.unix.shell
From: bet@ritz.mordor.com (Bennett Todd)
Subject: Re: "$@" versus ${1+"$@"}
Followup-To: comp.unix.shell
Date: Tue, 26 Sep 1995 14:35:45 GMT
Message-ID: <DFIoJL.934@ritz.mordor.com>

(This isn't really a TCL question; it's a Bourne Shell question; so I've cross-posted, and set followups, to comp.unix.shell).

Once upon a time (or so the story goes) there was a Bourne Shell somewhere which offered two choices for interpolating the whole command-line. The simplest was $*, which just borfed in all the args, losing any quoting that had protected internal whitespace. It also offered "$@", to protect whitespace. Now the icko bit is how "$@" was implemented. In this early shell, the two-character sequence $@ would interpolate as

$1" "$2" "$3" "$4" ... $n

so that when you added the surrounding quotes, it finished quoting the whole schmeer. Cute, cute, too cute.... Now consider what the correct usage

"$@"

will expand to if there are no args:

""

That's the empty string — a single argument of length zero. That's not the same as no args at all. So, someone came up with a clever application of another Bourne Shell feature, conditional interpolation. The idiom

${varname+value}

expands to value if varname is set, and nothing otherwise. Thus the idiom under discussion

${1+"$@"}

means exactly, precisely the same as a simple

"$@"

without that ancient, extremely weird bug.

So now the question: what shells had that bug? Are there any shells shipped with any even vaguely recent OS that included it?

-- 
-Bennett
bet@mordor.com
http://www.mordor.com/bet/
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜