[英]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.