除非特別說明,本文中出現的 Shell 均指 Bash 4.3。首先說一個基礎知識:Shell 中的變量在展開成值(Parameter Expansion)以後,這個值在某些上下文(Context)中,還會進行分詞操做(Word Splitting),但在另一些上下文中,不會進行分詞操做。本文中把會進行分詞操做的上下文叫作列表上下文(List Context),把不會進行分詞的上下文叫作標量上下文(Scalar Context)。還有一個基礎知識再提一嘴,就是 Shell 在分詞時會跳過那些被雙引號包圍的詞。數組
由於 $* 和 $@ 這兩個特殊變量在以上兩種上下文中的展開結果不同,因此下面必須分兩種狀況討論。編碼
列表上下文是咱們最熟悉的狀況,好比在簡單命令的參數中,又好比在 for-in 語句的參數中,這些地方須要的都是多個詞,因此 Shell 規定在這些地方要進行分詞操做。it
$* 在列表上下文中會展開成 $1 $2 $3 ... 多個詞,而又因列表上下文存在分詞操做,因此 $1 $2 等等都會再被 IFS 分割。io
$ set a "b c" dtable $ printf "%s\n" $* # $2 的值爲 "b c",但因爲 $2 自己沒有被雙引號包圍,因此會被分紅兩個詞 b c,因此一共就成了 a b c d 四個參數基礎 a變量 bList c總結 dtab |
"$*" 在列表上下文中會展開成 "$1c$2c$3...",c 是 IFS 的第一個字符,若是 IFS 爲空,則 c 也是空,若是 IFS 不存在,則 c 爲空格,雖然這裏存在分詞操做,但因爲展開後的值仍處於雙引號中,因此分詞操做不會有任何效果。
$ set a "b c" d $ IFS=: $ printf "%s\n" "$*" # 全部位置參數鏈接成了一個參數 a:b c:d |
$@ 在列表上下文中的表現和 $* 在列表上下文中的表現徹底同樣。
$ set a "b c" d $ printf "%s\n" $@ a b c d |
"$@" 在列表上下文中會展開成 "$1" "$2" "$3" ...,因爲展開後的每一個值都處於雙引號中,因此分詞操做不會有任何效果。
$ set a "b c" d $ printf "%s\n" "$@" a b c d |
列表上下文是咱們最熟悉的,Bash manual 對 $* 和 $@ 的講解也僅限於列表上下文中的表現,下面咱們講講它們倆在標量上下文中的表現。
最多見的標量上下文就是賦值語句的右邊,此外還有 case 關鍵字的後面,以及 [[ ]] 之間等等,這些地方須要的都是一個詞,因此 Shell 規定在這些地方不進行分詞操做。
$* 在標量上下文中展開成 $1c$2c$3...,c 是 IFS 的第一個字符,因爲標量上下文沒有分詞操做,因此這就結束了,也就是說,$* 在標量上下文的效果等同於 "$*" 在列表上下文中的效果。
$ set a "b c" d $ IFS=: $ var=$* # var 的值成了 "a:b c:d" $ echo "$var" a:b c:d |
"$*" 在標量上下文中展開成 "$1c$2c$3...",因爲反正沒有分詞操做,因此和 $* 在標量上下文中的表現同樣。因此也就是說 var=$* 和 var="$*" 徹底同樣。
$@ 在標量上下文展開成 $1空格$2空格$3...,這裏用「空格」字樣是爲了說明展開後的值是一個詞。也就是說,$@ 和 $* 在標量上下文下的區別僅僅是前者用空格作分隔符後者用 IFS 的第一個字符作分隔符這一個區別。
$ set a "b c" d $ IFS=: $ var=$* # var 的值成了 "a b c d",不使用 IFS $ echo "$var" a b c d |
"$@" 在標量上下文中展開成 "$1空格$2空格$3..." 和不加引號效果同樣,var=$@ 等效於 var="$@"。
再總結一下就是,在標量上下文中,$* 和 $@ 加不加引號都同樣,它倆的區別就是分隔符的區別,它倆展開後的結果都是用一個分隔符把全部位置參數鏈接成了一個詞。下面再用 [[ ]] 的代碼示例鞏固一下它倆的區別:
$ set a "b c" d $ IFS=: $ [[ "$*" == "a:b c:d" ]]; echo $? 0 $ [[ "$@" == "a b c d" ]]; echo $? 0 |
在實際編碼中不必記憶這些區別,你只須要記住一點,須要多個詞的時候用 "$@",須要一個詞的時候用 "$*",是的,永遠帶着引號。此外,因爲 Posix 規範明確規定了「本規範不對 $@ 在標量上下文上的表現作任何定義」,因此上面的一些代碼示例在 Bash 之外的 Shell 上可能有不一樣的結果。
最後一句,$* 和 $@ 的全部表現都應該能推廣到帶 * 和 @ 下標的任意數組上。