![${1+"$@"} 在 shell 脚本中意味着什么,它与 "$@" 有何不同?](https://linux22.com/image/30151/%24%7B1%2B%22%24%40%22%7D%20%E5%9C%A8%20shell%20%E8%84%9A%E6%9C%AC%E4%B8%AD%E6%84%8F%E5%91%B3%E7%9D%80%E4%BB%80%E4%B9%88%EF%BC%8C%E5%AE%83%E4%B8%8E%20%22%24%40%22%20%E6%9C%89%E4%BD%95%E4%B8%8D%E5%90%8C%EF%BC%9F.png)
在 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+"$@"}
工作。"$@"
如果我创建 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 = [ '' ];
互联网上的其他来源也使用
${1+"$@"}
,包括一名黑客谁似乎知道他在做什么。
也许${parameter+word}
是 的未记录的替代(或已弃用)语法${parameter:+word}
?有人可以证实这个假设吗?
答案1
这是为了与 Bourne shell 兼容。 Bourne shell 是一种古老的 shell,于 1979 年首次随 Unix 版本 7 发布,直到 90 年代中期/bin/sh
在大多数商业 Unices 上仍然很常见。
它是大多数人的祖先类似伯恩的贝壳喜欢ksh
,bash
或者zsh
。
它有一些尴尬的功能,其中许多已在ksh
其他 shell 和 的新标准规范中修复sh
,其中之一是:
对于 Bourne shell(至少是那些尚未修复的变体):"$@"
如果位置参数列表为空 ( $# == 0
) 而不是根本没有参数,则扩展为一个空参数。
${var+something}
$var
除非未设置,否则扩展为“某物” 。它在所有 shell 中都有明确记录,但在文档中很难找到,bash
因为你需要注意这句话:
当不执行子字符串扩展时,使用下面记录的形式,bash 测试未设置或为 null 的参数。 省略冒号会导致仅测试未设置的参数。
因此仅当set ( ) 时才${1+"$@"}
展开,它可以解决 Bourne shell 的限制。"$@"
$1
$# > 0
请注意,Bourne shell 是唯一存在该问题的 shell。 Modern sh
s(符合sh
POSIX 规范sh
(Bourne shell 不符合))不存在这个问题。因此,只有当您需要代码在非常旧的系统上工作时才需要这样做,其中/bin/sh
可能是 Bourne shell 而不是标准 shell(请注意,POSIX 不指定 standard 的位置sh
,因此例如在 Solaris 11 之前的 Solaris 上,/bin/sh
仍然是 Bourne shell(尽管没有那个特定问题),而普通/标准sh
位于另一个位置 ( /usr/xpg4/bin/sh
))。
尽管没有引用该perlrun
perldoc 页面,但存在一个问题。$0
答案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:+"$@"}' 与 "" 一起使用,它就不会$@
再扩展了。