將命令替換的輸出分配給變數時是否需要引用命令替換?

將命令替換的輸出分配給變數時是否需要引用命令替換?

即使將其輸出分配給變量,我也傾向於引用命令替換,如下所示:

var="$(command)"

但這實際上是需要的嗎?什麼時候破?接受的答案這裡索賠:

DIRNAME="$(目錄名 $FILE)"不會做你想做的事如果 $FILE 包含空格或通配符 [?*.

該連結指向 Grey Cat Wiki 關於引用的精彩頁面,但該頁面沒有具體提及引用命令替換。並且在引用的同時多變的顯然需要,引用命令替換本身似乎不需要。

然而,同一篇文章的結論是:

DIRNAME="$(dirname "$FILE")" 是推薦的方式。您可以用命令和空格替換 DIRNAME= 而不更改任何其他內容,並且 dirname 會收到正確的字串。

這也是我一直以來的想法,並且經常更正此處未引用它的帖子。然而,上面連結的維基頁面還聲稱:

在某些情況下,可以安全地省略雙引號:

在簡單作業的右側。您可以編寫不含引號的 foo=$bar 。這符合 POSIX 標準。

[。 。 。 ]

雖然var=$(command)這不是一個真正的「簡單」作業,但我仍然無法找到實際上需要引號的情況:

$ var=$(echo "foo bar baz")  ## whitespace works
$  echo "$var"
foo bar baz

$ var=$(printf "foo\nbar * baz") ## so do globbing characters
$ echo "$var"
foo
bar * baz

$ var1="foo\nbar * baz"
$ var=$(printf "$var1")  ## printing a variable doesn't make any difference
$ echo "$var" 
foo
bar * baz

$ var=$(printf '%s\n' "$var1")
$ echo "$var"
foo\nbar * baz

$ var=$(printf -- '-e %s\n' "$var1") ## strings starting with - also work
$ echo "$var"
-e foo\nbar * baz

當然,如果命令替換直接用於諸如 之類的東西command1 "$(command2)",那麼引號是絕對必要的,但在分配給變數時似乎並非如此。

那麼,我錯過了什麼?是否需要報價?什麼極端情況會引用命令替換將其傳回值指派給變數時保護你免受?或者,如果命令替換是變數賦值操作的右側,那麼不引用命令替換總是可以嗎?

答案1

你做不需要引用賦值語句右側的表達式。

令你惱火的是另一個答案推薦儘管如此。但這只是關於程式碼維護。

考慮以下正確的例子:

DIRNAME=$(dirname "$FILE")
echo "debug: dirname is $DIRNAME"
ls "$DIRNAME"

現在,使用此腳本一段時間後,您可能會認為可以刪除偵錯訊息。因此,使用編輯器您將刪除回顯線。然後您會注意到您甚至不再需要變數 DIRNAME,只需移動命令ls來替換分配的左側站點即可。現在你可以忘記添加所需的引號你最終會得到這個損壞的腳本

ls $(dirname "$FILE")

如果第一作者是 shell 專家而第二編輯是新手,則出現此類錯誤的可能性會更高。

當然這是否是值得商榷的建議避免使用便攜式 shell 功能真的很有用。就我個人而言,我主要按照他的建議去做。我也為更“簡單”的作業執行此操作,例如:(var="${foo}"也包括多餘的花括號)。

答案2

作為參考之一,Bash 的手冊對此說得很清楚

可以透過以下形式的語句將變數賦值給

name=[value]

如果未給出值,則為該變數指派空字串。所有值都會經歷波形符號擴展、參數和變數擴展、命令替換、算術擴展和引號刪除(詳細資訊如下)。 [...]不執行分詞,但「$@」除外,如下所述。不執行檔名擴充。

沒有分詞,沒有檔案名稱擴展,因此不需要引號。


至於 POSIX,部分2.9.1 簡單指令

2、非變數賦值或重定向的詞應展開。如果擴充後仍保留任何字段 如果擴充後仍保留任何字段,則第一個字段應被視為命令名稱,其餘字段是命令的參數。
[...]
4. 每個變數賦值都應在賦值之前擴展,以進行波形符擴展、參數擴展、命令替換、算術擴展和引號刪除。

我不確定這是否應該被解釋為字段分割僅發生在步驟 2 中完成的擴展中?第 4 步執行不是提到字段分割,儘管相關的部分場分裂也沒有提到變數賦值作為產生多個欄位的例外。

相關內容