形似賦值語句的引數

紫雲飛發表於2015-09-25

我們知道,在 Bash 中,當變數出現在一個賦值語句的右側時,變數只會展開,不會分詞,即便變數兩邊沒有引號: 

$ foo="1                       2"

$ bar=$foo # 不會被拆分成 bar=1 和 2 兩個詞

$ echo "$bar"

1                       2

但是,當一個形似賦值語句的詞,作為引數傳給一個命令時,這時分詞步驟就不會少了:

$ foo="1                       2"

$ printf '%s\n' bar=$foo

bar=1

2

bar=$foo 雖然看起來像是賦值語句,但因為它出現在了命令名稱的後面,所以它就是個普通的引數。它會先展開成 bar=1                       2,然後被拆分成 bar=1 和 2 兩個引數傳給 printf。外部命令也一樣:

$ valueOfa="1 b=2"

$ env -i a=$valueOfa env # 你以為傳給 env 的第二個引數是 "a=1 b=2",但其實是 "a=1" 和 "b=2" 兩個引數

a=1

b=2

可是,有一些命令卻是特殊的,比如 alias:

$ valueOfa="1 b=2"

$ alias a=$valueOfa # 傳給 alias 命令的會是 "a=1" 和 "b=2" 兩個引數?

$ alias

alias a='1 b=2'  # 並不是,valueOfa 這個變數居然沒有被分詞!

像 alias 這樣的內部命令一共有 6 個,分別是:alias、declare、typeset、export、readonly、local,它們的功能都是宣告一個什麼東西,變數或者別名。Bash 對它們的引數做了特殊對待,如果滿足賦值語句的格式,則不對其中的變數進行分詞。當然你也可以就把它們看成是賦值語句,反正賦值語句的效果是建立一個變數,這些引數的效果也是建立一個變數(除了 alias 命令)。Bash 手冊也直接把它們說成是賦值語句:

Assignment statements may also appear as arguments to the aliasdeclaretypeset,exportreadonly, and local builtin commands. 

在 Bash 的實現裡,是專門用一個函式對這樣的引數做了特殊處理:

/* This is a hack to suppress word splitting for assignment statements
   given as arguments to builtins with the ASSIGNMENT_BUILTIN flag set. */
static void
fix_assignment_words (words)
     WORD_LIST *words;
{
...

等等,還沒完。上面說,如果滿足賦值語句的格式,這些引數才會被特殊對待,那如果不滿足呢?

我們知道,賦值語句的左邊必須是合法的識別符號,合法的識別符號得符合“字母下劃線開頭後跟若干個字母數字下劃線”。這樣的話 1=$foo 就必定不是個合法的賦值語句了,下面試一把:

$ foo="1 2=2"

$ alias 1=$foo

$ alias

alias 1='1'

alias 2='2'

果然,被當成普通的引數了。

相關文章