bash是否在$ {var + ...}内错误地执行“ $ @”扩展?

时间:2018-08-27 21:20:18

标签: bash shell scripting posix expansion

我目前通过3个炮弹获得2条意见:

$ bash -c 'set bar; set foo${1+ "$@"}; echo "$# $*"'
1 foobar

$ ash -c 'set bar; set foo${1+ "$@"}; echo "$# $*"'
2 foo bar

$ dash -c 'set bar; set foo${1+ "$@"}; echo "$# $*"'
2 foo bar

还是我忽略了一些POSIX定义,它将我的示例呈现为实现定义的行为?

请注意,似乎只有“ $ @”会触发差异。以下所有3个Shell的工作原理都相同:

$ bash -c 'set bar; set foo${1+ $*}; echo "$# $*"'
2 foo bar

$ ash -c 'set bar; set foo${1+ $*}; echo "$# $*"'
2 foo bar

$ dash -c 'set bar; set foo${1+ $*}; echo "$# $*"'
2 foo bar

不幸的是,如果参数应包含空格,则$ *与“ $ @”不太相同。

BTW,我正在使用Bash 4.4.12(1)-发行版。

2 个答案:

答案 0 :(得分:0)

我倾向于不信任未加引号的参数扩展内的空格。我无法解释为什么它不起作用,但是我可以给你一个解决方案:将空间移到参数扩展之外。 set命令不介意尾随空白。

$ bash -c 'set bar; set foo ${1+"$@"}; echo "$# $*"'   # 5.0.2(1)
2 foo bar

$ dash -c 'set bar; set foo ${1+"$@"}; echo "$# $*"'   # 0.5.10.2
2 foo bar

$ ash  -c 'set bar; set foo ${1+"$@"}; echo "$# $*"'   # /bin/sh on FreeBSD 7.3
2 foo bar

$ ksh  -c 'set bar; set foo ${1+"$@"}; echo "$# $*"'   # 93u+ 2012-08-01
2 foo bar

$ zsh  -c 'set bar; set foo ${1+"$@"}; echo "$# $*"'   # 5.7
2 foo bar

(除了ash以外,我都在最新的Debian Testing中运行了所有内容)

答案 1 :(得分:0)

尽管这确实是一个错误,但我之前遇到过一些带有“ ... $ @”的怪异情况,并且至少在历史上与预期的行为相符。 " $@"(带有空白行)是未定义的行为,因为您基本上是在要求将$@数组的所有成员都视为shell解析器加上一个空格字符。可能有点难以理解,而且您所说的某些内容具有误导性,所以我想我会提供更多的信息和实现更好的提示。行为。

您使用“ $ @”并尝试将其与“ $ *”互换的方式会引起问题。 $*$@的行为在符合其预期用途的行为上有很大的不同。如果您使用的是$@,那是因为您想要执行以下操作:

wrapper_fxn() {
  wrapped_cmd "$@"
}

当您将$ @括在引号中(以及与此相关的任何其他数组)时,bash对其进行评估,就像将该数组的每个参数/成员首先用双引号引起来一样。但是,还涉及更多的魔术,因为它实际上并没有将其包装在双引号中,而是删除了双引号,但指示在以后的步骤中更改解析行为。

存在"$@"特殊行为的原因是为了帮助解决令人沮丧的quotingargv干扰问题。

所以,这样想吧:

  • $ @:多个args /,用于在保留空白引号的同时传播args
  • $ *:单个arg /用于将数组中的所有字符串串联为单个arg

我的建议:不要做"$@ ";这是未定义的行为。像"$@"一样引用时,您就尝试进行自变量扩展,并添加另一个自变量或自变量数组,就像这样:

# Making sure to have quotes around each new arg
somecmd "$newFirstArg" "$@" "${additionalArray[@]}" "$newLastArg"

...它将更好地传达意图,并且几乎永远不会产生意外行为。

深入了解

话虽如此,当您使用"... $@"时,“ $ @”的奇怪边缘行为必须要做,就像“ $ @”一样。 Bash会根据...与多余字符的位置合并数组的一项。

在带引号的语句{em> eg $@"extra stuff $@"中,在"$@ extra stuff"的左侧或右侧添加任何内容时,bash会执行以下操作:将extra stuff分别添加到数组的第一个或最后一个成员。例外情况是,如果双引号语句中还有另一个数组,也许$@像这样复制:"$@ $@"。实际上,它将合并第一个数组的最后一项和最后一个数组的第一项。因此,如果$@具有三个成员,则argv[]的最后"$@ $@"将具有5个参数,而不是6。

这是一个实用工具功能,可帮助您检查行为:

# example function
print-args () 
{ 
    local i=0;
    for arg in "$@";
    do
        printf "[${i}]:%s\n" "\"$arg\"";
        let i++;
    done
}

# good example arg-set covering many of the important edge cases
set "one \"one" two three

# One case, modify it to see the various permutations of the edge cases
print-args " $@ $@ "
#>[0]:" one "one"
#>[1]:"two"
#>[2]:"three one "one"
#>[3]:"two"
#>[4]:"three "