コマンド置換の出力を変数に割り当てるときに、コマンド置換を引用符で囲む必要がありますか?

コマンド置換の出力を変数に割り当てるときに、コマンド置換を引用符で囲む必要がありますか?

私は、出力を変数に割り当てるときでも、以下のようにコマンド置換を引用符で囲む傾向があります。

var="$(command)"

しかし、それは本当に必要なのでしょうか?いつ壊れるのでしょうか?受け入れられた答えここ請求:

DIRNAME="$(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")

最初の著者がシェルの専門家であるが、2 番目の編集者が初心者である場合、このような間違いが発生する可能性はさらに高くなります。

もちろん、これがポータブルシェル機能を避けるための推奨事項は本当に便利です。個人的には、私は主に彼の推奨通りにやっています。また、次のようなより「単純な」割り当てにもこれを行いますvar="${foo}"(余分な中括弧も含みます)。

答え2

参考までに、Bashのマニュアルにはこの点が明記されている:

変数は次のような形式で代入できる。

name=[value]

値が指定されていない場合、変数には null 文字列が割り当てられます。すべての値に対して、チルダ展開、パラメータと変数の展開、コマンド置換、算術展開、および引用符の削除が行われます (詳細は以下を参照)。 [...]単語分割は行われませんただし、以下で説明する「$@」は例外です。ファイル名の拡張は実行されません。

単語の分割やファイル名の拡張は行われないため、引用符は必要ありません。


POSIXに関しては、セクション2.9.1 シンプルなコマンド:

2. 変数の割り当てまたはリダイレクトではない単語は展開されます。展開後にフィールドが残っている場合、最初のフィールドはコマンド名と見なされ、残りのフィールドはコマンドの引数になります。
[...]
4. 各変数の割り当ては、値を割り当てる前に、チルダ展開、パラメータ展開、コマンド置換、算術展開、および引用符削除のために展開されます。

フィールド分割はステップ2で行われた拡張に対してのみ行われると解釈すべきかどうかわかりません。ステップ4ではないフィールド分割については触れているが、フィールド分割また、複数のフィールドを生成する例外として変数の割り当てについても言及されていません。

関連情報