Beefy Boxes and Bandwidth Generously Provided by pair Networks
The stupid question is the question not asked
 
PerlMonks  

Re: Running Under Some Shell

by rubasov (Friar)
on Feb 24, 2010 at 22:51 UTC ( [id://825214]=note: print w/replies, xml ) Need Help??


in reply to Running Under Some Shell

These "gubbins" are needed only when you encounter a platform which does not handle shebang lines (#!...). Those platforms generally will pass anything to the sh interpreter what does not look like a binary executable file (== does not have the format of an a.out or ELF ...). Therefore if you're sure you or your users won't execute those programs on such platforms then you can replace them safely with the simple shebang.

Let's investigate this (the first case coming from perlrun):

#!/usr/bin/perl eval 'exec /usr/bin/perl -wS $0 ${1+"$@"}' if $running_under_some_shell;

This code will be passed first to sh. sh will interpret the shebang line as a comment, then will look at the second line as a command terminated by the newline, so the if line is not relevant in this case.

If only sh would interpret this code, then the eval would not be needed and this would suffice:

exec /usr/bin/perl -wS $0 ${1+"$@"}

However later this line will be seen by perl also, that's why we should find a construct that is syntactically valid both in sh and in perl. The eval '...' construct fulfills this.

At this point sh is interpreting the eval line, not looking ahead what will follow in the source file, and never will encounter the if line because it is replacing itself with a perl process by using exec.

perl is called with two options (-w and -S), you already know the meaning of those. Then in the perl command line follows the name of the actual script ($0) and all the command line arguments passed to this script (${1+"$@"}).

"$@" expands to the positional parameters in sh terminology, so it is equivalent to "$1" "$2" "$3" ...

The ${parameter:+word} syntax is explained in the man page of bash (in the Parameter Expansion section):

${parameter:+word}
Use Alternate Value. If parameter is null or unset, nothing is substituted, otherwise the expansion of word is substituted.
And omitting the colon from the construct above is also explained there:
... bash tests for a parameter that is unset or null; omitting the colon results in a test only for a parameter that is unset

As I see ${1+"$@"} can be simplified to "$@". The only difference is that in the first case only $1 will be checked whether it is set.

Okay, if the exec succeeded then now we have a running perl processing the same file getting the same arguments. The shebang line is a comment for perl also, but the newline is not a statement separator in perl, so the first statemant is eval '...' if $running_under_some_shell;. As this variable will be always false, this is semantically a no-op, so perl will happily skip it, and execute the rest of the file.

If you write eval '...' if 0; it has the same meaning, but you won't get the warning $running_under_some_shell used only once.

I'm not really knowledgeable in the c shell, so I can't explain now the gubbin designed for sh and csh at the same time.

I hope this helps understanding what is going on.

Replies are listed 'Best First'.
Re^2: Running Under Some Shell
by JavaFan (Canon) on Feb 25, 2010 at 10:13 UTC
    As I see ${1+"$@"} can be simplified to "$@". The only difference is that in the first case only $1 will be checked whether it is set.
    Yes, that's my idea as well. If there's at least one argument, $1 is set and ${1+"$@"} first expands to "$@" which then expands to $1 $2 $3 .... If $1 isn't set ${1+"$@"} expands to nothing. In either case, it's the same as "$@". There might be some shell somewhere that makes a difference, but according my reading of a manual found on the Interwebs, even in the Bourne Shell on System 7 both ${1+"$@"} and "$@" are identical. The POSIX standard also says that in the absence of positional parameters, "$@" expands to nothing.
    I'm not really knowledgeable in the c shell, so I can't explain now the gubbin designed for sh and csh at the same time.
    I'd be surprised if the gubbin actually worked on csh. AFAIK, it doesn't do ${foo+bar} style parameter expansion (but I don't have a csh laying around to try it on).

    Perhaps the ${1+"$@"} is just a piece of cargo cult. Noone is really sure if it's going to break on some system somewhere, and just leaves it as is, instead of replacing it with "$@". It's not that there's a huge savings.

      I'd be surprised if the gubbin actually worked on csh.
      Oh sorry. I wanted to refer to another gubbin, namely the second example in perlrun, that's the "more baroque" in the OP.

      Perhaps the ${1+"$@"} is just a piece of cargo cult.
      Exactly the same thought here.

        Oh sorry. I wanted to refer to another gubbin, namely the second example in perlrun, that's the "more baroque" in the OP.
        Ah, you mean the
        eval '(exit $?0)' && eval 'exec perl -wS $0 ${1+"$@"}' & eval 'exec /usr/bin/perl -wS $0 $argv:q' if $running_under_some_shell;
        Let me try to explain. First, since "eval" is valid Perl, and so are the & and && operators, Perl will never execute any of it, because $running_under_some_shell is false.

        $?0 is special csh construct, returning 1 if the current filename is known, 0 otherwise. $? is the exit status of the most recent pipeline in sh. Since (exit $?0) is executed in a subshell (due to the parens), and from a file, eval '(exit $?0)' will return 0 in sh, but 1 in csh. This means, the eval 'exec perl -wS $0 ${1+"$@"}' is only executed by sh. But when executed by sh, sh will not see anything else of the file.

        Which leaves use with & eval 'exec /usr/bin/perl -wS $0 $argv:q'. I do not quite know how csh parses that - I can only assume csh allows a "null command" - which is then put in the background. Then it executes /usr/bin/perl -wS $0 $argv:q. We already know the meaning of the -wS $0 part. $argv is a variable holding all the arguments to the program. The :q is a modifier, quoting all the arguments (just like "$@" does in sh).

        The use of ${1+"$@"} is a bit of a cargo cult thing these days, but I can remember the reason for it, and it's not unthinkable that it is still needed on the kinds of platforms where the rest of the gubbins are necessary.

        Early versions of /bin/sh (this is not going back to V7; it was present in 4BSD and probably into the 1990s on many flavors of Unix - but not Linux) did not do the right thing when there were no arguments to be quoted. If you used "$@" when there were no arguments, this was replaced by an empty string - rather than by nothing at all. Needless to say, an empty string argument is rather different from no argument, compare e.g. cat and cat '' for a simple example.

        The ${1+"$@"} is used to expand to exactly nothing if the first argument is not set (the ${1+ } part does this), but if the first argument is set (i.e. there are some arguments) it expands into "$@" which expands into each of the arguments, quoted against being broken at whitespace.

        If you didn't care about the whitespace, you see, you could just as easily use $* with no quotes (and a lot of Unix folks who would sooner dine with the devil than put a space in a filename still write shell scripts that way). But if you remember the early early early days of Perl, when Larry Wall was better known for a shell script generating tool called Configure, you can imagine why this ${1+"$@"} is still used.

        Nowadays, of course, ksh and bash and pretty much any modern shell that claims POSIX 'sh' compatibility does the right thing with just "$@" and you can use that in your scripts in most situations.

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: note [id://825214]
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others admiring the Monastery: (4)
As of 2024-03-29 00:31 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found