gre*_*Dot 8 arrays bash shell windows-subsystem-for-linux
最近,我在计算机上设置了Microsoft的Windows Linux子系统。它只是模仿Linux环境和东西。基本上是Cygwin,但与底层Windows系统的连接要好一些。从Cygwin切换到WSL之后,我遇到了一个问题。我不知道它是否特定于Windows的实现,但这在Cygwin中不会发生。
为了更快地捕获代码中的错误,我开始使用bash的set -u选项,该选项使外壳“在替换时将未设置的变量视为错误”。否则,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??
Run Code Online (Sandbox Code Playgroud)
从的输出中可以看到declare -p array,bash 确实识别出数组为空和数组未设置之间的区别-直到需要真正对其进行扩展时,bash才适合。我知道bash 特别对待@and *变量,甚至在引用时也是如此,因此我尝试了很多东西。无效:
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
Run Code Online (Sandbox Code Playgroud)
奇怪的是,我可以访问数组的索引数组。但是,bash存在相反的问题,因为当要求输入未设置的数组的索引时,它也会成功执行:
Me@Computer:~$ echo "${!array[@]}"
==>
Me@Computer:~$ echo "${!unset_array[@]}"
==>
Run Code Online (Sandbox Code Playgroud)
(以上适用于数组扩展格式的所有变体。)
最令人沮丧的是,我什至无法访问空数组的长度:
Me@Computer:~$ echo "${#array[@]}"
==> bash: array[@]: unbound variable
Run Code Online (Sandbox Code Playgroud)
格式的所有变体也都失败了。
有人知道为什么会这样吗?这是一个错误,还是这种预期的行为?如果是后者,动机是什么?有什么方法可以禁用此行为,让我保持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
Run Code Online (Sandbox Code Playgroud)
(请注意,重置位置参数时,仍然需要使用欺骗手段,以防万一它们原来是空的。)每次使用可能为空的数组时,都需要执行这些扭曲操作。
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
Run Code Online (Sandbox Code Playgroud)
这并不特定于是否在 WSL 下运行 Bash,而是取决于 Bash 版本。
该行为已被报告为 Bash 4.1 的错误,但被认为是预期行为。Chet 还指出 和 的不同行为$@是$*因为 POSIX 强制要求它。当时推荐的解决方法与安迪的评论类似,是:
echo ${argv[0]+"${argv[@]}"}
Run Code Online (Sandbox Code Playgroud)
它扩展为"${argv[@]}"ifargv已设置,否则没有其他内容(请注意外部扩展未被引用)。
在 Bash 4.4 中,行为发生了变化,如CHANGES中所述,从 bash-4.4-beta2 更改为 bash-4.4-rc2,作为“新功能”:
当启用该选项时,使用
${a[@]}或${a[*]}使用没有任何分配元素的数组nounset不会再引发未绑定变量错误。