`
liumengfan
  • 浏览: 33142 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

${1+"$@"}

阅读更多
今天看到erl启动脚本的时候,看到

ROOTDIR="/usr/local/lib/erlang"
BINDIR=$ROOTDIR/erts-5.10.2/bin
EMU=beam
PROGNAME=`echo $0 | sed 's/.*\///'`
export EMU
export ROOTDIR
export BINDIR
export PROGNAME
exec "$BINDIR/erlexec" ${1+"$@"}

这个脚本首先初始化一些环境变量,其中:
PROGNAME=`echo $0 | sed 's/.*\///'`

是深处符号"."以及后面的所有字符。而${1+"$@"}则就是$@,具体的解释参照下面的文章
引用

What does ${1+"$@"} mean

...and where is it necessary?

2012-06-16 (see recent changes)

It's a workaround for older releases of the traditional Bourne shell: they parse "$@" in an unexpected way if no arguments were supplied.
All traditional Bourne shells since the SVR3 release and all modern bourne compatible shells behave intuitively.
One might argue, whether the old behaviour is a bug in the very sense, or unintuitive de-facto behaviour. But it was not properly specified, yet, and definitely error-prone.

(It's also a workaround for a problem in various shells if you want to use set -u. See more at the bottom of this page, "Oh wait, ...")

Example
in a shell, where this workaround is necessary.

With the following script,

$ cat args.sh
for i in "$@"; do
    echo arg: \"$i\"
done

you get output as expected if you provide at least one argument:

$ sh args.sh 1 2
arg: "1"
arg: "2"

However, without arguments, you still get (empty) output, instead of no output at all:

$ sh args.sh
arg: ""

Why is this relevant?

Keep in mind, that "$@" is most frequently used for passing on arguments, or for iterating over them in a loop, like above.
Thus a mechanism for collapsing the argument list to nothing--and not to the empty argument--is needed.

How does it work?

The ${1+"$@"} syntax first tests if $1 is set, that is, if there is an argument at all.
If so, then this expression is replaced with the whole "$@" argument list.
If not, then it collapses to nothing instead of an empty argument.

Why does it work this way?
The problem only arises if a variable is quoted, and $1 is not.

The workaround is not limited to this variant. These work also: ${@+"$@"} and ${*+"$@"},
but the abovementioned variant is the widely known one.

Another, historic problem

Even with the above workaround, early shells before SVR2 (7th edition, BSDs, SysIII and SVR1) discard empty arguments in the argument list.

$ sh ./args.sh 1 '' 2
arg: "1"
arg: "2"

This looks quite similar at first, but in practice it's less relevant, because dealing without any argument is much more likely to happen.

Portability

${1+"$@"} is reliable and portable -- with one exception:

zsh until release 4.3.0, in sh emulation mode (which means the option shwordsplit is set), does word splitting on the resulting arguments.

$ sh ./args.sh '1 2'
arg: "1"
arg: "2"

An alternative

You might just loop yourself over the arguments:

    for i
    do
echo "$i"
    done

interestingly also valid if merged but without semicolon:

    for i do
echo "$i"
    done

Although these are not documented for the Version 7 Bourne shell (and its variants on early BSDs), they work on these systems, too.
(Picked up from the german lang usenet posting <3BE2D2D3.I715123W@bigfoot.de> from Gunnar Ritter; and I could confirm this on all the flavours).

See more details about this short form of the for-loop at the end of this page.

In contrast to ${1+"$@"}, these variants even work correctly in the pre-SVR2 variants if there are empty arguments in the list (pointed out by Stéphane Chazelas and I can confirm it).
However, for passing on the arguments to another program (instead of iterating over them) ${1+"$@"} (or "$@") is certainly the only solution.

Which Bourne shells exactly need the workaround?

These shells behave the old way and need ${1+"$@"}:

    Certainly: /bin/sh on 7th edition (aka Version 7). And thus also /bin/sh on original BSDs, until these shipped the Almquist shell (after 4.3BSD-Reno)
    HP-UX 8, 9 /bin/sh, HP-UX 10.x, 11.x /usr/old/bin/sh (as /bin/sh has become a ksh)
    OSF1/V4 and V5 aka Tru64 /bin/sh
    Ultrix /bin/sh and /bin/sh5
    Sinix 5.20 /bin/sh in both the "ucb" and the "sie" universe

In contrast, these Bourne shell variants behave the modern way:

    8th edition (aka Version /bin/sh
    SunOS 4.1.x and SunOS 5.x /bin/sh
    IRIX 5 - IRIX 6.3 /bin/sh, IRIX 6.4+x /usr/bin/bsh (as /usr/bin/sh has become a ksh)
    AIX 3.2.4 /bin/sh, AIX 4.x /usr/bin/bsh (as /usr/bin/sh has become a ksh)
    OpenServer 3 ff. /usr/bin/sh
    MUNIX 3.1 (Cadmus, SVR3.1) /bin/sh
    Sinix 5.20 /bin/sh in the "xopen" universe

Oh wait, there's another meaning

It's also a workaround for a problem in some shells if you want to use the flag u ("error upon using empty variables")
and "$@" (or $@, "$*", $*) is used without arguments. Example:

    $ shell -cu 'echo     "$@";  echo not reached'
    @: parameter not set
    $ shell -cu 'echo ${1+"$@"}; echo ok'
    ok

These shells for example need that workaround:

    bash-4.0.0 ... -4.0.27
    Bourne shells (traditional)
    dash-0.4.6 ... -0.4.18
    ksh88
    ksh93 until release t+20090501
    mksh (as pdksh descendant)
    NetBSD 2 ff. /bin/sh
    pdksh-5.1.3, -5.2.14
    posh < 0.10 (as pdksh descendant)

Endnote

Details about the short form of the for-loop.

Among the four variations

      $ for i
do
      $ for i do
      $ for i; do
      $ for i;
        do

    The original 7th ed Bourne shell and all its later flavours don't accept the third and fourth variant [1].
    Interestingly, the second variant is accepted (which isn't consistent with accepting the first, but consistent with not accepting the third).

    ksh86, ksh88a ff. and ksh93(d..u) accept all but the fourth variant.
    The reason might be that the code is partly based on the Bourne shell.

    The original Almquist shell (ash) accepts all but the second variant.
    It just handles the terminator sort of consistently.

    The almquist shells since 4.4BSD and 386BSD (and thus all descendants except Minix pre-3.1.3)
    accept all four variants, apparently fixed for backwards portability or flexibility.

    All other shells I tried accept all four variants (apparently intentionally being backwards portable or flexible, too),
    bash-1.05 ff., pdksh-5.2.14 (thus mksh, posh.), zsh-3.0 ff.

    The third variant is not POSIX conformant.
    Update: Jens Schweikhardt reported it as issue, and Jilles Tjoelker mentioned the fourth variant, which I added here.

[1] This is weird because ; (semicolon) and newline are usually equivalent.

The documentation doesn't explicitly tell if a terminator is required/allowed; it seems to read as if it's not allowed

     for name [in word ...] do list done
          Each time a for command is executed name is set to  the
          next word in the for word list If in word ...  is omit-
          ted then in "$@" is assumed.  Execution ends when there
          are no more words in the list.

Comparison with the while control command suggests itself, but is not helpful. The while description doesn't match behaviour, because the terminator in list is not optional
(and because it's incorrect anyway, because "do list" is not optional as written, which will be fixed in the System III documentation),

     while list [do list] done

     A list is a sequence of one or more pipelines  separated  by
     ;, &, && or || and optionally terminated by ; or &.    [...]
             Newlines may appear in  a  list,  instead  of  semi-
     colons, to delimit commands. 


分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics