再談 和 在 Bash 中的表現

2021-09-07 02:24:22 字數 2319 閱讀 6880

除非特別說明,本文中出現的 shell 均指 bash 4.3。首先說乙個基礎知識:shell 中的變數在展開成值(parameter expansion)之後,這個值在某些上下文(context)中,還會進行分詞操作(word splitting),但在另外一些上下文中,不會進行分詞操作。本文中把會進行分詞操作的上下文叫做列表上下文(list context),把不會進行分詞的上下文叫做標量上下文(scalar context)。還有乙個基礎知識再提一嘴,就是 shell 在分詞時會跳過那些被雙引號包圍的詞。

因為 $* 和 $@ 這兩個特殊變數在以上兩種上下文中的展開結果不一樣,所以下面必須分兩種情況討論。

列表上下文是我們最熟悉的情況,比如在簡單命令的引數中,又比如在 for-in 語句的引數中,這些地方需要的都是多個詞,所以 shell 規定在這些地方要進行分詞操作。

$* 在列表上下文中會展開成 $1 $2 $3 ... 多個詞,而又因列表上下文存在分詞操作,所以 $1 $2 等等都會再被 ifs 分割。

$ set a "b c" d

$ printf "%s\n" $* # $2 的值為 "b c",但由於 $2 本身沒有被雙引號包圍,所以會被分成兩個詞 b c,所以一共就成了 a b c d 四個引數ab

cd"$*" 在列表上下文中會展開成 "$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" $@ab

cd"$@" 在列表上下文中會展開成 "$1" "$2" "$3" ...,由於展開後的每個值都處於雙引號中,所以分詞操作不會有任何效果。

$ set a "b c" d

$ printf "%s\n" "$@"

ab 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 $?

$ [[ "$@" == "a b c d" ]]; echo $?

在實際編碼中沒必要記憶這些區別,你只需要記住一點,需要多個詞的時候用 "$@",需要乙個詞的時候用 "$*",是的,永遠帶著引號。此外,由於 posix 規範明確規定了「本規範不對 $@ 在標量上下文上的表現做任何定義」,所以上面的一些**示例在 bash 以外的 shell 上可能有不同的結果。

最後一句,$* 和 $@ 的所有表現都應該能推廣到帶 * 和 @ 下標的任意陣列上。 

和 在巨集替換中的作用

include define f a,b a b define g a a define h a g a int main 首先需要了解 和 的意義。將右邊的引數做整體的字串替換。define g a a 則g hello world hello world g sleep 1 sleep 1 對於...

MVVM模式和在WPF中的實現

我大概是從2102年底開始接觸wpf,之前一直用winform。剛開始看了下感覺跟winform區別不大,控制項可以拖進去,選中了控制項屬性面板可以設定屬性 事件面板可以監聽事件,後台 處理事件,一切都那麼的熟悉。xaml布局也跟android布局很像,所以沒學習就直接開始了,覺得摸索摸索基本就差不...

C 和 在巨集定義中的作用

將右邊的引數做整體的字串替換。define string x x x define text x name x inttest 將左右兩邊的引數做整體的字串拼接替換。define class name name class name define merge a,b a b a inttest 對於...