English 中文(简体)
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/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 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/




相关问题
Why does my chdir to a filehandle not work in Perl?

When I try a "chdir" with a filehandle as argument, "chdir" returns 0 and a pwd returns still the same directory. Should that be so? I tried this, because in the documentation to chdir I found: "...

How do I use GetOptions to get the default argument?

I ve read the doc for GetOptions but I can t seem to find what I need... (maybe I am blind) What I want to do is to parse command line like this myperlscript.pl -mode [sth] [inputfile] I can use ...

Object-Oriented Perl constructor syntax and named parameters

I m a little confused about what is going on in Perl constructors. I found these two examples perldoc perlbot. package Foo; #In Perl, the constructor is just a subroutine called new. sub new { #I ...

Where can I find object-oriented Perl tutorials? [closed]

A Google search yields a number of results - but which ones are the best? The Perl site appears to contain two - perlboot and perltoot. I m reading these now, but what else is out there? Note: I ve ...

热门标签