まず、zshを残りから分離します。古いシェルと最新のシェルの問題ではありません。zshの動作は異なります。zshデザイナーは、従来のシェル(Bourne、ksh、bash)との互換性をなくし、使いやすくすることにしました。
第二に、二重引用符をいつ使用するかは、いつ必要かを覚えるよりもずっと簡単です。それらはほとんどの場合必要であるため、必要なときではなく、必要でないときを学ぶ必要があります。
一言で言えば、単語のリストまたはパターンが予想される場合は常に二重引用符が必要です。生の文字列がパーサーによって予期されるコンテキストでは、これらはオプションです。
引用符なしで何が起こるか
二重引用符がないと、2つのことが起こります。
- 最初に、展開の結果(などのパラメーター置換の変数の値
${foo}
、またはのようなコマンド置換のコマンドの出力$(foo)
)は、空白が含まれているすべての場所で単語に分割されます。
より正確には、展開の結果は、IFS
変数の値に含まれる各文字(区切り文字)で分割されます。区切り文字のシーケンスに空白(スペース、タブ、または改行)が含まれる場合、空白は単一の文字としてカウントされます。先頭、末尾、または繰り返される非空白区切り文字は、空のフィールドにつながります。例えば、とIFS=" :"
、:one::two : three: :four
前に空のフィールドを生成するone
間、one
とtwo
の間、および(単一のもの)three
とfour
。
- 分割の結果の各フィールドは、文字のいずれかを含む場合、グロブ(ワイルドカードパターン)として解釈され
\[*?
ます。そのパターンが1つ以上のファイル名に一致する場合、パターンは一致するファイル名のリストに置き換えられます。
引用符で囲まれていない変数展開$foo
は、「split + glob operator」として通俗的に知られて"$foo"
いますが、対照的に変数の値を取得するだけですfoo
。コマンド置換についても同じことが言えます。"$(foo)"
コマンド置換で$(foo)
あり、コマンド置換の後にsplit + globが続きます。
二重引用符を省略できる場所
以下に、二重引用符なしで変数またはコマンド置換を記述できるBourneスタイルのシェルで考えられるすべてのケースを示します。値は文字どおりに解釈されます。
割り当ての右側。
var=$stuff
a_single_star=*
export
キーワードではなく、通常の組み込み関数であるため、の後に二重引用符が必要なことに注意してください。これは、ダッシュ、zsh(shエミュレーション)、yash、またはposhなどの一部のシェルでのみ当てはまります。bashとkshは両方ともexport
特別に扱います。
export VAR="$stuff"
でcase
声明。
case $var in …
ケースパターンには二重引用符が必要であることに注意してください。単語の分割はケースパターンでは発生しませんが、引用符で囲まれていない変数はパターンとして解釈され、引用符で囲まれた変数はリテラル文字列として解釈されます。
a_star='a*'
case $var in
"$a_star") echo "'$var' is the two characters a, *";;
$a_star) echo "'$var' begins with a";;
esac
二重括弧内。二重括弧は、シェルの特別な構文です。
[[ -e $filename ]]
パターンまたは正規表現が予想される場所で二重引用符が必要な場合を除きます:=
or ==
または!=
orの右側=~
。
a_star='a*'
if [[ $var == "$a_star" ]]; then echo "'$var' is the two characters a, *"
elif [[ $var == $a_star ]]; then echo "'$var' begins with a"
fi
二重引用符は、通常の[ … ]
シェル構文(たまたま呼び出されるコマンドです[
)であるため、通常は単一の括弧内に二重引用符が必要です。単一または二重括弧を参照してください
非対話型POSIXシェルでのリダイレクト(bash
ではなくksh88
)。
echo "hello world" >$filename
一部のシェルは、対話型の場合、変数の値をワイルドカードパターンとして扱います。POSIXは非対話型シェルでのその動作を禁止していますが、bash(POSIXモードを除く)やksh88(sh
Solarisなどの一部の商用Unicesの(おそらく)POSIXとして検出された場合を含む)を含むいくつかのシェルはまだそこにあります(分割bash
しようとしますそして、その分割+グロビングが正確に1つの単語にならない限りリダイレクトは失敗します)、それはsh
あなたがbash
スクリプトにいつかそれをスクリプトに変換したい場合はスクリプトでリダイレクトのターゲットを引用する方が良い理由sh
ですその点で非準拠であるか、対話型シェルから供給される場合があります。
算術式の中。実際、変数を算術式として解析するには、引用符を省略する必要があります。
expr=2*2
echo "$(($expr))"
ただし、ほとんどのシェルではPOSIXが必要とする(!?)
連想配列の添字。
typeset -A a
i='foo bar*qux'
a[foo\ bar\*qux]=hello
echo "${a[$i]}"
引用符で囲まれていない変数とコマンド置換は、まれな状況で役立つ場合があります。
- 変数値またはコマンド出力がグロブパターンのリストで構成されており、これらのパターンを一致するファイルのリストに拡張する場合。
- 値にワイルドカード文字が含まれていないことがわかっている場合、それ
$IFS
は変更されず、空白文字で分割する必要があります。
- 特定の文字で値を分割する場合:でグロビングを無効にし、区切り文字に
set -f
設定IFS
して(または空白のままで分割するにはそのままにして)、展開します。
Zsh
zshでは、いくつかの例外を除いて、ほとんどの場合二重引用符を省略できます。
$var
複数の単語に展開されることはありませんが、値がvar
空の文字列の場合、空のリストに展開されます(単一の空の単語を含むリストとは対照的です)。コントラスト:
var=
print -l $var foo # prints just foo
print -l "$var" foo # prints an empty line, then foo
同様に、"${array[@]}"
配列のすべての要素に展開しますが、$array
空でない要素にのみ展開します。
@
パラメータ展開フラグは時々全体の置換の周りに二重引用符が必要です"${(@)foo}"
。
引用符で囲まれていない場合は、コマンド置換は、フィールド分割を受ける:echo $(echo 'a'; echo '*')
プリントa *
(単一のスペースを伴う)のに対しecho "$(echo 'a'; echo '*')"
、未修飾2行の文字列を印刷します。"$(somecommand)"
最後の改行を除いて、単一の単語でコマンドの出力を取得するために使用します。"${$(somecommand; echo _)%?}"
最終改行を含むコマンドの正確な出力を取得するために使用します。"${(@f)$(somecommand)}"
コマンドの出力から行の配列を取得するために使用します。
SH_WORD_SPLIT
オプションの影響を受けます。