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

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

在 Perl 文档中,佩尔伦(1)建议使用双语 shell/Perl 标头启动 Perl 脚本:

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

这是什么${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);
    

    和 Question.sh 为

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

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

    $ ./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. 互联网上的其他来源也使用${1+"$@"},包括一名黑客谁似乎知道他在做什么。

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

答案1

这是为了与 Bourne shell 兼容。 Bourne shell 是一种古老的 shell,于 1979 年首次随 Unix 版本 7 发布,直到 90 年代中期/bin/sh在大多数商业 Unices 上仍然很常见。

它是大多数人的祖先类似伯恩的贝壳喜欢kshbash或者zsh

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

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

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

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

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

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

尽管没有引用该perlrunperldoc 页面,但存在一个问题。$0

http://www.in-ulm.de/~mascheck/various/bourne_args/了解更多信息。

答案2

两者之间存在差异:

command ""

command

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

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

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

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

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

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

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

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

答案3

我总结了 Stéphane Chazelas 的回答:

  • ${1:+"$@"}' 测试 $1 是否为空或未设置
  • ${1+"$@"}' 测试 $1 是否未设置

因此,如果使用第二个参数“”,则意味着$1为空,但它不会测试它是否为空,它只是看到它已经被设置,但它是否为空,因此它将扩展$@ ,但是你将 ${1:+"$@"}' 与 "" 一起使用,它就不会$@再扩展了。

相关内容