Tell me more ×
Unix & Linux Stack Exchange is a question and answer site for users of Linux, FreeBSD and other Un*x-like operating systems.. It's 100% free, no registration required.

In the Perl documentation, perlrun(1) suggests launching Perl scripts using a bilingual shell/Perl header:

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

What does ${1+"$@"} mean? I tried using "$@" instead (using Bash as /bin/sh), and it seems to work just as well.


Edit

Two answers below say that it's supposed to be ${1:+"$@"}. I am aware of the ${parameter:+word} ("Use Alternate Value") syntax documented in bash(1). However, I am unconvinced, because

  1. Both ${1+"$@"} and "$@" work just fine, even when there are no parameters. If I create simple.sh as

    #!/bin/sh
    eval 'exec /usr/bin/perl -x -S -- $0 "$@"'
        if 0;
    #!perl
    use Data::Dumper;
    print Dumper(\@ARGV);
    

    and question.sh as

    #!/bin/sh
    eval 'exec /usr/bin/perl -x -S -- $0 ${1+"$@"}'
        if 0;
    #!perl
    use Data::Dumper;
    print Dumper(\@ARGV);
    

    I can get both to work identically:

    $ ./question.sh 
    $VAR1 = [];
    $ ./question.sh a
    $VAR1 = [
              'a'
            ];
    $ ./question.sh a 'b c'
    $VAR1 = [
              'a',
              'b c'
            ];
    $ ./question.sh ""
    $VAR1 = [
              ''
            ];
    $ ./simple.sh 
    $VAR1 = [];
    $ ./simple.sh a
    $VAR1 = [
              'a'
            ];
    $ ./simple.sh a 'b c'
    $VAR1 = [
              'a',
              'b c'
            ];
    $ ./simple.sh ""
    $VAR1 = [
              ''
            ];
    
  2. Other sources on the Internet also use ${1+"$@"}, including one hacker who seems to know what he's doing.

Perhaps ${parameter+word} is an undocumented alternate (or deprecated) syntax for ${parameter:+word}? Could someone confirm that hypothesis?

share|improve this question
Looks like grawlixes – moose Mar 22 at 9:57

3 Answers

up vote 16 down vote accepted

That's for compatibility with the Bourne shell. The Bourne shell was an old shell that was first released with Unix version 7 in 1979 and was still common until the mid 90s as /bin/sh on most commercial Unices.

It is the ancestor of most Bourne-like shells like ksh, bash or zsh.

It had a few awkward features many of which have been fixed in ksh and the other shells and the new standard specification of sh, one of which is this:

With the Bourne shell (at least those variants where it has not been fixed): "$@" expands to one empty argument if the list of positional parameters is empty ($# == 0) instead of no argument at all.

${var+something} expands to "something" unless $var is unset. It is clearly documented in all shells but hard to find in the bash documentation as you need to pay attention to this sentence:

When not performing substring expansion, using the forms documented below, bash tests for a parameter that is unset or null. Omitting the colon results in a test only for a parameter that is unset.

So ${1+"$@"} expands to "$@" only if $1 is set ($# > 0) which works around that limitation of the Bourne shell.

Note that the Bourne shell is the only shell with that problem. Modern shs (that is sh conforming to the POSIX specification of sh (which the Bourne shell is not)) don't have that issue. So you only need that if you need your code to work on very old systems where /bin/sh might be a Bourne shell instead of a standard shell (note that POSIX doesn't specify the location of the standard sh, so for instance on Solaris before Solaris 11, /bin/sh was still a Bourne shell (though did not have that particular issue) while the normal/standard sh was in another location (/usr/xpg4/bin/sh)).

There is a problem in that perlrun perldoc page in that $0 is not quoted though.

See http://www.in-ulm.de/~mascheck/various/bourne_args/ for more information.

share|improve this answer
Can't reproduce in Heirloom. Probably not applicable to Solaris. – ormaaj Mar 19 at 21:18
@ormaaj. Yes, see the page I linked. It was fixed in SVR3, And Solaris/SunOS above 5 rebased on SVR4. And that page mentions that 4.1.x didn't have the issue either. – Stephane Chazelas Mar 19 at 21:21
Ah I see. <3 Mascheck pages. – ormaaj Mar 19 at 21:38

There is a different between:

command ""

and

command

In one, you are passing one argument that is an empty string. In the second, there are zero arguments passed.

For both, "$@" will equate to the same thing: "". But using ${1:+"$@"} would be "" for the first and no arguments passed for the second, which was the intent.

This becomes important if you are doing something the script below, called sshwrapper, which you call with an optional command or no arguments to get an interactive shell.

: sshwrapper [command]
exec /usr/bin/ssh "${HOSTNAME:-localhost}" "$@"

This would attempt to run "" on the remote host (which just returns), it would not be an interactive shell.

: sshwrapper [command]
exec /usr/bin/ssh "${HOSTNAME:-localhost}" ${1:+"$@"}

Would start an interactive shell on the remote host because the exec would correctly interpret the variable as nothing, not an empty string.

Read up on the usage of "${parameter:+word}" ("Use Aternate Value") and similar variable expansion strings in the bash manual pages.

share|improve this answer

EDIT: Turns out I already knew the answer to this and simply forgot due to it being such a useless piece of knowledge... See: http://mywiki.wooledge.org/WrapperScript (looks like I last edited this over a year ago)

Now that I know this is a wrapper, it looks like a horrible idea. Anybody with such absurd portability requirements to need ${1+$@} is not going to want to rely upon the value of $0.

I don't really understand the point of this wrapper, but the best solution is to forget the wrapper and use whatever Perl provides to modify its own options and arguments. If it has to be a wrapper, either embed the Perl program into a heredoc, or use a separate file in a known location. If the point is all about resolving a specific Perl version from PATH, let env do it... it's portable enough.


It means whoever wrote the script didn't know what they were doing. That syntax in general means to expand the alternate if the parameter 1 is unset. One reason you might use that is to expand the alternate within quotes, but to not expand an empty argument if the parameter is unset, for instance, ${1+"$1"} makes sense. However, @ within quotes expands to no words if no positional parameters are set, so this is pointless syntax.

Another reason people sometimes use this is to protect unset variables when set -u is enabled (set -u is a horrible hack that should preferably not be used, my opinion on this here). However, that problem doesn't apply to @ and *, so that can't be the reason.

My guess would be that either they really intended to use :+ in order to not expand an empty argument if the first parameter is set to empty, or they just aren't aware of the behavior of a quoted "$@" when there's nothing to expand.

I should also add that the eval in that expression is also completely pointless. It's identical in every way to no eval with the single-quotes removed.

There are many possible tricks with this expansion. I use it frequently personally, but overall it's probably the least common next to the ? operator.

share|improve this answer
the eval is used to make it both a valid perl and sh syntax. – Stephane Chazelas Mar 19 at 21:44
The problem with set -u applies in some shells for $@, see in-ulm.de/~mascheck/various/bourne_args – Stephane Chazelas Mar 19 at 21:45
[ -n "${var+x}" ] is the only portable way to check whether a variable is set, so it's a relatively common operator. – Stephane Chazelas Mar 19 at 21:47
@StephaneChazelas Interesting about the eval. I'd think that would likely cause a problem when the Perl comes across it. I would never use it for this purpose. WRT set -u, can't reproduce in mksh (only shell listed there in which it says it hasn't been fixed). WRT checking unset vars, I typically use ! ${var+false} for POSIX, which performs best in most shells especially in the true case. ${var+:} false is another alternative. – ormaaj Mar 19 at 21:57
eval is a valid function in both perl and sh. But in perl , because of the if 0, it's not executed while it is in sh which is the whole point of that trick. – Stephane Chazelas Mar 19 at 22:15
show 3 more comments

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.