${1+"$@"} 在 shell 脚本中是什么意思,它与 "$@" 有何不同?

200*_*ess 47 shell bash perl

在 Perl 文档中,perlrun(1)建议使用双语 shell/Perl 头文件启动 Perl 脚本:

#!/bin/sh
#! -*-perl-*-
eval 'exec perl -x -wS $0 ${1+"$@"}'
    if 0;
Run Code Online (Sandbox Code Playgroud)

什么${1+"$@"}意思?我尝试"$@"改用(使用 Bash 作为 /bin/sh),它似乎也能正常工作。


编辑

下面的两个答案说它应该是${1:+"$@"}. 我知道${parameter:+word}bash(1) 中记录的(“使用替代值”)语法。然而,我不相信,因为

  1. 无论${1+"$@"}"$@"工作得很好,即使在没有参数。如果我创建 simple.sh 作为

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

    和 question.sh 作为

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

    我可以让两者以相同的方式工作:

    $ ./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 = [
              ''
            ];
    
    Run Code Online (Sandbox Code Playgroud)
  2. 互联网上的其他来源也使用${1+"$@"},其中包括一位似乎知道自己在做什么的黑客

也许${parameter+word}是未记录的替代(或弃用)语法${parameter:+word}? 有人能证实这个假设吗?

Sté*_*las 57

这是为了与 Bourne shell 兼容。Bourne shell 是一个旧的 shell,它于 1979 年首次随 Unix 版本 7 一起发布,直到 90 年代中期仍然像/bin/sh大多数商业 Unices一样常见。

它是大多数类似 Bourne 的 shell的祖先,例如ksh,bashzsh

它有一些笨拙的功能,其中许多功能已在ksh其他 shell 和 的新标准规范中得到修复sh,其中之一是:

使用 Bourne shell(至少那些尚未修复的变体):"$@"如果位置参数列表为空 ( $# == 0) 而不是根本没有参数,则扩展为一个空参数。

${var+something}除非$var未设置,否则扩展为“某物” 。它在所有 shell 中都有清楚的记录,但在文档中很难找到,bash因为您需要注意这句话:

当不执行子字符串扩展时,使用下面记录的形式,bash 测试未设置或为空的参数。 省略冒号会导致仅对未设置的参数进行测试

因此${1+"$@"}扩展为"$@"仅当$1设置为 ( $# > 0) 时,它可以解决 Bourne shell 的限制。

请注意,Bourne shell 是唯一存在该问题的 shell。Modern shs(sh符合 POSIX 规范sh(Bourne shell 不符合))没有这个问题。因此,如果您需要您的代码在/bin/sh可能是 Bourne shell 而不是标准 shell 的非常旧的系统上工作,您只需要这样做(请注意,POSIX 没有指定标准的位置,sh例如在 Solaris 11 之前的 Solaris 上,/bin/sh仍然是 Bourne shell(虽然没有那个特殊问题),而正常/标准sh在另一个位置(/usr/xpg4/bin/sh))。

但是,该perlrunperldoc 页面中存在一个问题,$0但未引用该页面。

有关更多信息,请参阅http://www.in-ulm.de/~mascheck/various/bourne_args/

  • @ormaaj。是的,请参阅我链接的页面。它在SVR3中得到了修复,并且Solaris/SunOS 5以上重新基于SVR4。该页面提到 4.1.x 也没有问题。 (2认同)

Arc*_*ege 5

两者之间存在差异:

command ""
Run Code Online (Sandbox Code Playgroud)

command
Run Code Online (Sandbox Code Playgroud)

在其中一种情况下,您传递的参数是一个空字符串。在第二个中,传递了零个参数。

对于两者来说,“$@”将等同于同一件事:""。但使用${1:+"$@"}将用于""第一个,并且不会为第二个传递任何参数,这就是意图。

如果您正在执行下面的脚本(称为 sshwrapper),您可以使用可选命令或不带参数来调用该脚本来获取交互式 shell,那么这一点就变得很重要。

: sshwrapper [command]
exec /usr/bin/ssh "${HOSTNAME:-localhost}" "$@"
Run Code Online (Sandbox Code Playgroud)

这将尝试在远程主机上运行“”(仅返回),它不是交互式 shell。

: sshwrapper [command]
exec /usr/bin/ssh "${HOSTNAME:-localhost}" ${1:+"$@"}
Run Code Online (Sandbox Code Playgroud)

将在远程主机上启动交互式 shell,因为 exec 会正确地将变量解释为空,而不是空字符串。

阅读 bash 手册页中的“${parameter:+word}”(“使用替代值”)和类似变量扩展字符串的用法。

  • 不,如果 `$1` 为空,`${1:+"$@"}` 将扩展为无参数。您想要`${1+"$@"}`。基本上你把它颠倒了。 (4认同)