Bash

a=’$@’ 的意外結果

  • May 18, 2018

我在這種情況下苦苦掙扎:

$ set -- 1 2 3
$ a="$@"
$ echo "$a"
1 2 3

我發現出乎意料的是任務本身。

man bash關於"$@"擴展是這樣說的:

當擴展出現在雙引號內時,每個參數都擴展為一個單獨的單詞。

所以這應該類似於:

b="1" "2" "3"
bash: 2: command not found

這就是"$*"擴展的目的,我收集到:

當擴展出現在雙引號內時,它會擴展為一個單詞,每個參數的值由 IFS 特殊變數的第一個字元分隔。那是, ” $ *" is equivalent to " $ 1c$2c…",其中 c 是 IFS 變數值的第一個字元。如果未設置 IFS,則參數以空格分隔。如果 IFS 為空,則連接參數而不插入分隔符。

所以這應該是正確的:

$ set -- 1 2 3
$ a="$*"
$ echo "$a"
1 2 3

那麼為什麼"$@"產量相同呢?他們應該在這一點上有所不同。這是 Bash 問題還是我的誤解?

Shellcheck 將此檢測為SC2124。我還可以提供一個觸發SC2145的範例。

觀察到:

GNU bash, version 4.4.12(1)-release (x86_64-pc-linux-gnu)
4.9.0-6-amd64 #1 SMP Debian 4.9.82-1+deb9u3 (2018-03-02) x86_64 GNU/Linux

據我所知,POSIX 留下$@了一個未定義的賦值,所以它不是一個真正的錯誤,除了可能在 Bash 的文件中。$@在兩種情況下定義:

  • 當擴展發生在將執行欄位拆分的上下文中時……
  • 當擴展發生在雙引號內時,行為是未指定的,除非$$ … $$ 將執行欄位拆分$$ … $$ (*)

但,

在所有其他上下文中,擴展的結果是未指定的。

欄位拆分不會發生在賦值中,即使沒有雙引號也不會發生,所以這是未定義的。


現在,我假設它的a="$@"行為方式與a="$*"因為擴展到多個單詞在這裡沒有任何意義。您不能將多個單詞作為不同的實體分配給正常變數,並且分配一個但將其餘單詞用作命令參數會令人困惑並且容易出現錯誤。

正如那個 shellcheck 頁面所說,"$@"分配中的行為在 shell 之間有所不同。Bash 和 ksh 用空格連接位置參數,zsh 和破折號用第一個字母 of 連接IFS(就像"$*"會做的那樣)。

$ bash -c 'set -- x y z; IFS=.; a="$@"; printf "<%s>\n" "$a"'
<x y z>
$ ksh93 -c 'set -- x y z; IFS=.; a="$@"; printf "<%s>\n" "$a"'
<x y z>
$ zsh -c 'set -- x y z; IFS=.; a="$@"; printf "<%s>\n" "$a"'
<x.y.z>
$ dash -c 'set -- x y z; IFS=.; a="$@"; printf "<%s>\n" "$a"'
<x.y.z>

a="$*"如果您想加入單個字元串,或者以其他方式明確編寫您想要的內容,最好使用它。

(*或其他涉及${parameter:-word}擴展的情況)

引用自:https://unix.stackexchange.com/questions/444654