
我試圖了解 bash 中的 shell 擴展(GNU bash,版本 4.4.20(1)-release (i686-pc-linux-gnu))。
在我的互動式 bash shell 中輸入
x='$(id)'
$x
$(echo $x)
我預計最後兩行中的任何一行都會出現以下形式的錯誤
bash: uid=xxx(user): command not found
但得到了
bash: $(id): command not found
。
我不明白為什麼這裡不發生命令替換。不是應該透過變數擴展來實現嗎?我的猜測是它與 Shell 操作有關,如這裡所述https://www.gnu.org/savannah-checkouts/gnu/bash/manual/bash.html#Shell-Operation
有人可以解釋這種行為嗎?
我只是有興趣更準確地理解 bash 擴展。我對在我的問題中運行實際腳本不感興趣。
答案1
$(…)
是命令替換(「進程替換」是<(…)
等等)。變數替換和命令替換發生在同一遍中,在字串中從左到右。這些替換結果中唯一發生的事情是分詞和通配符。
因此x='$(id)'
設定x
為 5 個字元的字串$(id)
。然後,為了運行$x
,shell 將替換$x
為值$(id)
。它不包含任何空格或通配符,因此它被視為命令名稱。
對比:
x='@(id)'
shopt -s extglob
echo /none/$x /usr/bin/$x
假設該文件/none/id
不存在但/usr/bin/id
確實存在,該echo
命令將擴展為三個單字:(echo
顯然)、/none/@(id)
(glob 模式/none/@(id)
不匹配任何內容,因此保持不變)和/usr/bin/id
(glob 模式/usr/bin/@(id)
匹配一個文件,因此將其替換為匹配的單元素列表)。
在bash手冊中,相關的句子位於外殼擴展部分。
展開的順序是:大括號展開;波形符擴展、參數和變數擴展、算術擴展和命令替換(以從左到右的方式完成);分詞;和檔案名稱擴充。
兩個分號之間的所有內容都是一次傳遞。每一遍都基於前一遍的結果。
請注意,單一句子(即使是像我上面引用的那樣複雜的句子)無法講述整個故事。 shell 語意很複雜。我懷疑任何 shell 的手冊都包含所有特殊情況的詳細資訊。這POSIX規範更正式,但不涵蓋 bash 特定的擴展,甚至它留下了一些未定義的非常奇怪的情況。
答案2
這是引用。單引號 ( '
) 定義文字字串,且不能發生插值或轉義。雙引號 ( "
) 允許插值和轉義。
這是一個例子:
$ x='$(id)'
$ echo 'The variable x contains the value \"$x\"'
The variable x contains the value \"$x\"
$ echo "The variable x contains the value \"$x\""
The variable x contains the value "$(id)"
$ x="$(id)"
$ echo "The variable x contains the value \"$x\""
The variable x contains the value "uid=1000(myusername)..."
這是基於單引號與雙引號的插值和轉義的另一個範例:
$ echo 'The current directory is $PWD according to the variable \"\$PWD\".'
The current directory is $PWD according to the variable \"\$PWD\".
$ echo "The current directory is $PWD according to the variable \"\$PWD\"."
The current directory is /tmp according to the variable "$PWD".