简体   繁体   English

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

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

Recently, I set up Microsoft's Windows Subsystem for Linux on my computer.最近,我在我的电脑上设置了微软的Windows 子系统 Linux It just emulates a Linux environment and stuff;它只是模拟 Linux 环境和其他东西; basically, it's Cygwin, but a little better connected to the underlying Windows system.基本上,它是 Cygwin,但更好地连接到底层 Windows 系统。 After switching from Cygwin to WSL, however, I ran into a problem.然而,从 Cygwin 切换到 WSL 后,我遇到了一个问题。 I don't know if it's particular to Windows' implementation or not, but this doesn't happen in Cygwin.我不知道它是否特定于 Windows 的实现,但这不会发生在 Cygwin 中。

To catch bugs in my code a little faster, I've taken to using bash's set -u option, which causes the shell to "treat unset variables as an error when substituting."为了更快地发现代码中的错误,我开始使用 bash 的set -u选项,这会导致 shell“在替换时将未设置的变量视为错误”。 Without this, bash treats unset variables as variables set to the empty string when expanding them.如果没有这个,bash 在扩展它们时会将未设置的变量视为设置为空字符串的变量。

However, this has an odd unintended consequence (at least on WSL) with respect to arrays:然而,这对数组有一个奇怪的意外后果(至少在 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??

As you can see from the output of declare -p array , bash does recognize the difference between array being empty and array being unset—until it comes time to actually expand it, whereupon bash throws a fit.正如您从declare -p array的输出中看到的那样,bash确实识别出数组为空和数组未设置之间的区别——直到需要实际扩展它为止,因此 bash 抛出了一个合适的问题。 I know bash treats the @ and * variables specially, and even more so when quoted, so I tried a bunch of stuff.我知道 bash 特别对待@*变量,在引用时更是如此,所以我尝试了很多东西。 Nothing works:没有任何效果:

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

Oddly enough, I can access the array of indices of the array;奇怪的是,我可以访问数组的索引数组; however, bash then has the opposite problem in that it also succeeds when asked for the indices of an unset array:然而,bash 然后有相反的问题,因为它在询问未设置数组的索引时也会成功:

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

(The above works for all variations of the array expansion formats.) (以上适用于数组扩展格式的所有变体。)

Most frustratingly, I can't even access the length of an empty array:最令人沮丧的是,我什至无法访问空数组的长度:

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

This too fails with all of the variations of the format.对于格式的所有变体,这也失败了。

Does anyone know why this is happening?有谁知道为什么会这样? Is it a bug, or is this expected behavior?这是一个错误,还是这是预期的行为? If it's the latter, what's the motivation?如果是后者,动机是什么? Are there any ways to disable this behavior that allow me to keep set -u ?有什么方法可以禁用这种行为,让我保持set -u吗?


Workaround(s):解决方法:

I hit upon a really crappy workaround taking advantage of the fact that the positional parameters are immune to this phenomenon.利用位置参数不受这种现象影响的事实,我找到了一个非常糟糕的解决方法。 If anyone finds a better one, please let me know!如果有人找到更好的,请告诉我!

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

(Note that you still need to use trickery when resetting the positional parameters, just in case they themselves were originally empty.) These contortions would need to be performed every time a potentially empty array was used. (请注意,在重置位置参数时您仍然需要使用技巧,以防它们本身最初为空。)每次使用可能为空的数组时都需要执行这些扭曲。


Other notes:其他注意事项:

  • test -v also thinks empty arrays are unset, unlike declare -p . test -v也认为空数组是未设置的,与declare -p不同。
  • The same problems occur with associative arrays.关联数组也会出现同样的问题。
  • I tried initializing the array with declare (ie, declare -a array=( ) ), but that changed nothing.我尝试使用declare初始化数组(即declare -a array=( ) ),但这没有任何改变。
  • The positional parameter arrays, thankfully, seem to be immune from this phenomenon.幸运的是,位置参数数组似乎不受这种现象的影响。
  • I thought of just using "${array[@]-}" whenever I wanted to access an array, but this won't work in all scenarios.每当我想访问数组时,我都想过使用"${array[@]-}" ,但这不适用于所有场景。 "${array[@]}" , when double quoted, is supposed to expand as separate words for each array element; "${array[@]}" ,当双引号时,应该为每个数组元素扩展为单独的单词; an empty array, then, should be expanded into 0 words (compare set -- "$@";echo $# with set -- "$*";echo $# ).然后,一个空数组应该扩展为 0 个单词(比较set -- "$@";echo $#set -- "$*";echo $# )。 "${array[@]-}" , however, expands into a single word, the empty string.但是, "${array[@]-}"扩展为一个单词,即空字符串。

Version & environment info:版本和环境信息:

Like I said at the top, I'm using the Windows Subsystem for Linux on Windows 10. Other info:就像我在顶部所说的那样,我在 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

This isn't specific to Bash running under WSL or not, but depends on the Bash version.这不是特定于在 WSL 下运行的 Bash,而是取决于 Bash 版本。

The behaviour has been reported as a bug for Bash 4.1, but was considered intended behaviour .该行为已被报告为 Bash 4.1 的错误,但被认为是预期行为 Chet also points out that the different behaviour for $@ and $* is because POSIX mandates it. Chet 还指出$@$*的不同行为是因为 POSIX 强制要求它。 The recommended workaround back then, similar to Andy's comment, was:当时推荐的解决方法,类似于安迪的评论,是:

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

which expands to "${argv[@]}" if argv is set, and nothing otherwise (notice the outer expansion being unquoted).如果设置了argv则扩展为"${argv[@]}" ,否则不扩展(注意外部扩展未加引号)。

In Bash 4.4, the behaviour changed, as documented in CHANGES , from bash-4.4-beta2 to bash-4.4-rc2, as a "new feature":在 Bash 4.4 中,行为改变了,如CHANGES 中所述,从 bash-4.4-beta2 到 bash-4.4-rc2,作为“新功能”:

Using ${a[@]} or ${a[*]} with an array without any assigned elements when the nounset option is enabled no longer throws an unbound variable error.当启用nounset选项时,将${a[@]}${a[*]}与没有任何分配元素的数组一起使用不再引发未绑定的变量错误。

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

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