二重引用符「[[」内ではなく、単一括弧「[」内ではなく引用符なしのスペースを使用したパラメーター展開が機能するのはなぜですか?


86

単一または二重の括弧の使用と混同しています。このコードを見てください:

dir="/home/mazimi/VirtualBox VMs"

if [[ -d ${dir} ]]; then
    echo "yep"
fi

文字列にスペースが含まれていても、完全に機能します。しかし、単一のブラケットに変更すると:

dir="/home/mazimi/VirtualBox VMs"

if [ -d ${dir} ]; then
    echo "yep"
fi

それは言います:

./script.sh: line 5: [: /home/mazimi/VirtualBox: binary operator expected

次のように変更すると:

dir="/home/mazimi/VirtualBox VMs"

if [ -d "${dir}" ]; then
    echo "yep"
fi

正常に動作します。誰かが何が起こっているのか説明できますか?"${var}"スペースに起因する問題を防ぐために、変数を二重引用符で囲む必要があるのはいつですか?



回答:


83

単一のブラケット[は実際にはtestコマンドのエイリアスであり、構文ではありません

単一ブラケットの(多くの)欠点の1つは、評価しようとしている1つ以上のオペランドが空の文字列を返す場合、2つのオペランド(バイナリ)を期待していると文句を言うことです。オペランドが空の文字列に決して評価されないことを保証するため[ x$foo = x$blah ]、人々がそうするのを見る理由ですx

[[ ]]一方、二重括弧は構文であり、よりもはるかに機能し[ ]ます。お気づきのとおり、単一オペランドの問題はなく、演算子を使用したCライクな構文も可能です>, <, >=, <=, !=, ==, &&, ||

私の推奨事項は次のとおりです。インタープリターがの#!/bin/bash場合、常に使用します[[ ]]

それはそれを注意することが重要である[[ ]]、すべてのPOSIXシェルではサポートされていない、しかし、多くのシェルは、次のようなことをサポートしないzshkshに加えて、bash


16
空の文字列(および他の多くの)問題は、引用符を使用して解決されます。:「X」は、問題の別の種類包含するものであるオペランドはとしてもよい演算子。ときと同様$fooである!か、(または-n...その問題は、引数の数(横にPOSIXシェルに問題があることを意味しない[])が4以下です。
ステファンシャゼル

21
明確にするために[ x$foo = x$blah ]は、とまったく同じように間違ってい[ $foo = $bar ]ます。[ "$foo" = "$bar" ]任意のPOSIX準拠したシェルで、正確である[ "x$foo" = "x$bar" ]任意のボーンのようなシェルで動作しますが、xあなたは空の文字列を持っている場合のためではなく、どこ$fooかもしれ!-n...
ステファンChazelas

1
この答えの興味深いセマンティクス。私はあなたがそれが* not *構文であることの意味がわかりません。もちろん、[のエイリアスではありませんtest。もしそうならtest、閉じ角括弧を受け入れます。test -n foo ]。しかしtest[必要ありません。他のすべての点で[同じですがtest、それはどのようにalias機能しません。bashは説明[testとしてシェル組み込みコマンドが、[[シェルキーワード
小次郎

1
さて、bashの[は組み込みのシェルですが、/ usr / bin / [も実行可能ファイルです。これは伝統的に/ usr / bin / testへのリンクですが、最新のgnu coreutilsでは別個のバイナリです。テストの従来のバージョンは、argv [0]を調べて、[として起動されるかどうかを確認し、次に一致する]を探します。
エヴァン

動作しません私はbash >=<=
学生

52

[コマンドは通常のコマンドです。ほとんどのシェルは、効率のために組み込みとして提供しますが、シェルの通常の構文規則に従います。[は、最後の引数としてa test[必要とし、必要]testしないことを除いて、とまったく同じです。

二重括弧[[ … ]]は特別な構文です。これらは、正しく使用するのが面倒であり、シェル特殊文字を使用するいくつかの新しい追加ができる[ため、ksh(数年後)で導入されました。たとえば、次のように書くことができます[[[

[[ $x = foo && $y = bar ]]

全体の条件式がシェルによって解析されているので、一方で[ $x = foo && $y = bar ]最初の2つのコマンドに分割される[ $x = foo$y = bar ]により分離&&演算子。同様に二重括弧は、パターンマッチング構文のようなもの、例えばを有効に[[ $x == a* ]]する値かどうかをテストするxと始まりますa。単一の括弧内a*aは、現在のディレクトリで名前が始まるファイルのリストに展開されます。二重括弧はkshで初めて導入され、ksh、bash、およびzshでのみ使用可能です。

単一の括弧内では、他のほとんどの場所と同様に、変数置換の周りに二重引用符を使用する必要があります。これは、変数がコマンド(たまたま[コマンド)の引数にすぎないためです。二重括弧内では、二重引用符は必要ありません。シェルは単語の分割やグロビングを行わないためです。コマンドではなく条件式を解析します。

ただし例外[[ $var1 = "$var2" ]]は、バイト単位の文字列比較を行う場合に引用符が必要な場合です。そうでない場合は、照合$var2するパターンになります$var1

できないことの1つ[[ … ]]は、変数を演算子として使用することです。たとえば、これは完全に合法です(ただし、ほとんど役に立ちません)。

if [ -n "$reverse_sort" ]; then op=-gt; else op=-lt; fi

if [ "$x" "$op" "$y" ]; then 

あなたの例では

dir="/home/mazimi/VirtualBox VMs"
if [ -d ${dir} ]; then 

内部コマンドがifある[4つの引数で-d/home/mazimi/VirtualBoxVMs]。シェルは解析され-d /home/mazimi/VirtualBox、何をすべきかわかりませんVMs${dir}適切な形式のコマンドを取得するには、単語の分割を防ぐ必要があります。

一般的に、結果に対して単語の分割とグロビングを実行することがわかっている場合を除き、変数とコマンドの置換は常に二重引用符で囲みます。二重引用符を使用しないことが安全である主な場所は次のとおりです。

  • 割り当て内:(foo=$barただし、export "foo=$bar"などの配列割り当て内または二重引用符が必要なことに注意してくださいarray=("$a" "$b"));
  • case声明:case $foo in …;
  • 右側のを除いて二重括弧の内側=または==:演算子(あなたはパターンマッチングを行いたい場合を除きます)[[ $x = "$y" ]]

これらすべてで、二重引用符を使用するのが正しいので、高度なルールをスキップして、常に引用符を使用することもできます。


1
@StephaneChazelas私の間違い。それはそれは結局のところ[、1981年に実際にシステムIIIからである私はそれが年上だと思いました。
ジル

8

"${var}"スペースに起因する問題を防ぐために、変数を二重引用符で囲む必要があるのはいつですか?

この質問の暗黙は

なぜ十分ではないのですか?${variable_name}

${variable_name} あなたが思っていることを意味するものではありません…

...あなたはそれを持っていると思えば何でも(変数の値の)スペースによって引き起こされる問題をどうするの。 これに適しています:${variable_name}

$ bar=foo
$ bard=Shakespeare
$ echo $bard
Shakespeare
$ echo ${bar}d
food

何もありません!1  は、変数名の一部である可能性のある文字(文字(-または-)、アンダースコア()、または数字(-))をすぐに続けない限り、何の役にも立ちません。そして、それでも、それを回避できます:${variable_name}AZaz_09

$ echo "$bar"d
food

私はその使用を思いとどまらせるつもりはありません- echo "${bar}d"おそらくここでの最良の解決策です-しかし、人々引用符の代わりに中括弧頼るのを思いとどまらせるか、本能的に中括弧を適用してから、「今、引用符必要ですか?」  正当な理由がない限り、常に引用符を使用する必要があります。また、自分が何をしているのかを確実に知っている必要があります。
_________________
1    もちろん、パラメータ拡張のより手の込んだ形式、 およびなど が構文に基づいているという事実を除きます。また、10、11などの定位置パラメーターを参照するには、などを使用する必要があります。引用符はそれを助けません。${parameter:-[word]}${parameter%[word]}${parameter}${10}${11}


弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.