繁体   English   中英

为什么空数组在 bash 中被视为未设置?

[英]Why are empty arrays treated as unset in bash?

最近,我在我的电脑上设置了微软的Windows 子系统 Linux 它只是模拟 Linux 环境和其他东西; 基本上,它是 Cygwin,但更好地连接到底层 Windows 系统。 然而,从 Cygwin 切换到 WSL 后,我遇到了一个问题。 我不知道它是否特定于 Windows 的实现,但这不会发生在 Cygwin 中。

为了更快地发现代码中的错误,我开始使用 bash 的set -u选项,这会导致 shell“在替换时将未设置的变量视为错误”。 如果没有这个,bash 在扩展它们时会将未设置的变量视为设置为空字符串的变量。

然而,这对数组有一个奇怪的意外后果(至少在 WSL 上):

Me@Computer:~$ set -u
==>
Me@Computer:~$ declare -p array
==> bash: declare: array: not found
Me@Computer:~$ array=( )
==>
Me@Computer:~$ declare -p array
==> declare -a array='()'
Me@Computer:~$ echo "${array[@]}"       # Expands to "echo" (with 0 args), right?
==> bash: array[@]: unbound variable    # Wrong! wtf, bash??

正如您从declare -p array的输出中看到的那样,bash确实识别出数组为空和数组未设置之间的区别——直到需要实际扩展它为止,因此 bash 抛出了一个合适的问题。 我知道 bash 特别对待@*变量,在引用时更是如此,所以我尝试了很多东西。 没有任何效果:

Me@Computer:~$ echo "${array[@]}"
==> bash: array[@]: unbound variable
Me@Computer:~$ echo "${array[*]}"
==> bash: array[*]: unbound variable
Me@Computer:~$ echo ${array[@]}
==> bash: array[@]: unbound variable
Me@Computer:~$ echo ${array[*]}
==> bash: array[*]: unbound variable

奇怪的是,我可以访问数组的索引数组; 然而,bash 然后有相反的问题,因为它在询问未设置数组的索引时也会成功:

Me@Computer:~$ echo "${!array[@]}"
==>
Me@Computer:~$ echo "${!unset_array[@]}"
==>

(以上适用于数组扩展格式的所有变体。)

最令人沮丧的是,我什至无法访问空数组的长度:

Me@Computer:~$ echo "${#array[@]}"
==> bash: array[@]: unbound variable

对于格式的所有变体,这也失败了。

有谁知道为什么会这样? 这是一个错误,还是这是预期的行为? 如果是后者,动机是什么? 有什么方法可以禁用这种行为,让我保持set -u吗?


解决方法:

利用位置参数不受这种现象影响的事实,我找到了一个非常糟糕的解决方法。 如果有人找到更好的,请告诉我!

Me@Computer:~$ tmp=( "$@" )                    # Stash the real positional params; we need that array
Me@Computer:~$ set --                          # "$@" is now empty.
Me@Computer:~$ example_cmd "${array[@]-$@}"    # Now expands w/out error *and* w/ the right number of args
Me@Computer:~$ set -- "${tmp-$@}"              # Put the positional params back where we found them
Me@Computer:~$ unset tmp                       # Cleaning up after ourselves

(请注意,在重置位置参数时您仍然需要使用技巧,以防它们本身最初为空。)每次使用可能为空的数组时都需要执行这些扭曲。


其他注意事项:

  • test -v也认为空数组是未设置的,与declare -p不同。
  • 关联数组也会出现同样的问题。
  • 我尝试使用declare初始化数组(即declare -a array=( ) ),但这没有任何改变。
  • 幸运的是,位置参数数组似乎不受这种现象的影响。
  • 每当我想访问数组时,我都想过使用"${array[@]-}" ,但这不适用于所有场景。 "${array[@]}" ,当双引号时,应该为每个数组元素扩展为单独的单词; 然后,一个空数组应该扩展为 0 个单词(比较set -- "$@";echo $#set -- "$*";echo $# )。 但是, "${array[@]-}"扩展为一个单词,即空字符串。

版本和环境信息:

就像我在顶部所说的那样,我在 Windows 10 上使用适用于 Linux 的 Windows 子系统。其他信息:

Me@Computer:~$ bash --version
==> GNU bash, version 4.3.48(1)-release (x86_64-pc-linux-gnu)
    ...
Me@Computer:~$ echo "$-"
==> himuBCH

这不是特定于在 WSL 下运行的 Bash,而是取决于 Bash 版本。

该行为已被报告为 Bash 4.1 的错误,但被认为是预期行为 Chet 还指出$@$*的不同行为是因为 POSIX 强制要求它。 当时推荐的解决方法,类似于安迪的评论,是:

echo ${argv[0]+"${argv[@]}"}

如果设置了argv则扩展为"${argv[@]}" ,否则不扩展(注意外部扩展未加引号)。

在 Bash 4.4 中,行为改变了,如CHANGES 中所述,从 bash-4.4-beta2 到 bash-4.4-rc2,作为“新功能”:

当启用nounset选项时,将${a[@]}${a[*]}与没有任何分配元素的数组一起使用不再引发未绑定的变量错误。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM