为什么在bash中将空数组视为未设置? [英] Why are empty arrays treated as unset in bash?

查看:104
本文介绍了为什么在bash中将空数组视为未设置?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

最近,我在计算机上设置了Microsoft的 Windows Linux子系统。它只是模仿Linux环境和东西。基本上是Cygwin,但与底层Windows系统的连接要好一些。从Cygwin切换到WSL之后,我遇到了一个问题。我不知道它是否特定于Windows的实现,但这在Cygwin中不会发生。

Recently, I set up Microsoft's Windows Subsystem for Linux on my computer. It just emulates a Linux environment and stuff; basically, it's Cygwin, but a little better connected to the underlying Windows system. After switching from Cygwin to WSL, however, I ran into a problem. I don't know if it's particular to Windows' implementation or not, but this doesn't happen in Cygwin.

为了更快地捕获代码中的错误,我已经习惯使用bash的 set -u 选项,这导致外壳程序将未设置的变量作为替换时的错误进行处理。否则,bash在扩展它们时会将未设置的变量视为设置为空字符串的变量。

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." Without this, bash treats unset variables as variables set to the empty string when expanding them.

但是,相对于以下情况,这会带来奇怪的意外结果(至少在WSL上如此)数组:

However, this has an odd unintended consequence (at least on WSL) with respect to arrays:

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数组的输​​出中可以看到,bash 确实会认识到数组为空和数组未设置之间的区别-直到需要真正对其进行扩展时,bash才适合。我知道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. I know bash treats the @ and * variables specially, and even more so when quoted, so I tried a bunch of stuff. 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

很奇怪,我可以访问数组的索引数组;但是,bash存在相反的问题,因为当要求未设置数组的索引时,也会成功:

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:

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.

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

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?

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

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.


  • test -v 也认为未设置空数组,与 declare -p 不同

  • 关联数组也会出现相同的问题。

  • 我尝试使用声明(即 declare -a array =()),但是什么也没改变。

  • 位置参数数组似乎很不错到b可以避免这种现象。

  • 我想在需要访问时只使用 $ {array [@]-} 数组,但这不适用于所有情况。 $ {array [@]} 应该用双引号扩展为每个数组元素的单独单词;然后,将一个空数组扩展为0个字(比较 set- $ @; echo $# set- $ *; echo $#)。 $ {array [@]-} ,但是扩展为一个单词,即空字符串。

  • test -v also thinks empty arrays are unset, unlike declare -p.
  • The same problems occur with associative arrays.
  • I tried initializing the array with declare (i.e., declare -a array=( )), but that changed nothing.
  • 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[@]}", when double quoted, is supposed to expand as separate words for each array element; an empty array, then, should be expanded into 0 words (compare set -- "$@";echo $# with set -- "$*";echo $#). "${array[@]-}", however, expands into a single word, the empty string.

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

Like I said at the top, I'm using the Windows Subsystem for Linux on Windows 10. Other info:

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


推荐答案

这并非特定于在WSL下运行的Bash

This isn't specific to Bash running under WSL or not, but depends on the Bash version.

该行为一直是报告为Bash 4.1的错误,但考虑了预期的行为。 Chet还指出, $ @ $ * 的不同行为是因为POSIX要求它。类似于Andy的评论,当时建议的解决方法是:

The behaviour has been reported as a bug for Bash 4.1, but was considered intended behaviour. Chet also points out that the different behaviour for $@ and $* is because POSIX mandates it. The recommended workaround back then, similar to Andy's comment, was:

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

会扩展为 $ {argv [@] } 如果设置了 argv ,则没有其他设置(请注意外部扩展名未引用)。

which expands to "${argv[@]}" if argv is set, and nothing otherwise (notice the outer expansion being unquoted).

在Bash 4.4中,行为已更改,如 CHANGES 所述,从bash-4.4-beta2到bash-4.4-rc2,作为新功能:

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":


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

Using ${a[@]} or ${a[*]} with an array without any assigned elements when the nounset option is enabled no longer throws an unbound variable error.

这篇关于为什么在bash中将空数组视为未设置?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆