簡體   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