web-dev-qa-db-ja.com

二重角括弧[[]]はBashの単一角括弧[]よりも望ましいですか

同僚は最近コードレビューで[[ ]]構文が次のような構文で[ ]より優先されるべきであると主張しました

if [ "`id -nu`" = "$someuser" ] ; then 
     echo "I love you madly, $someuser"
fi

彼は理論的根拠を提供できなかった。ありますか?

459
Leonard

[[は驚きが少なく、一般的に安全です。しかし、移植性はありません - POSIXはそれが何をするのかを明記せず、いくつかのシェルだけがそれをサポートします(bashの他に、kshもそれをサポートすると聞きました)。たとえば、

[[ -e $b ]]

ファイルが存在するかどうかをテストします。しかし[では、引数を分割して$bのように展開するので("a*"は文字通り)、[[を引用符で囲む必要があります。これは[が外部プログラムになり、他のすべてのプログラムと同じようにその引数を受け取る方法とも関係しています(ただし組み込みプログラムになることもできますが、それでもこの特別な処理は行われません)。

[[には、=~との正規表現マッチングや、Cのような言語で知られている演算子など、他にもいくつかのNice機能があります。これが良いページです: test、[[[の違いは何ですか? and Bash Tests

519

行動の違い

Bash 4.3.11のいくつかの違い:

  • POSIX対Bash拡張機能:

  • 通常コマンドvsマジック

    • [は、奇妙な名前の通常のコマンドです。

      ][の単なる引数であり、これ以上の引数が使用されないようにします。

      Ubuntu 16.04には、coreutilsが提供する/usr/bin/[に実際に実行可能ファイルがありますが、bashの組み込みバージョンが優先されます。

      Bashがコマンドを解析する方法に変更はありません。

      特に、<はリダイレクト、&&および||は複数のコマンドを連結し、( )\でエスケープされない限りサブシェルを生成し、Wordの展開は通常どおり行われます。

    • [[ X ]]は、Xを魔法のように解析する単一の構成体です。 <&&||、および()は特別に扱われ、Wordの分割ルールは異なります。

      ==~などの違いもあります。

      Basheseの場合:[は組み込みコマンドであり、[[はキーワードです。 https://askubuntu.com/questions/445749/whats-the-difference-between-Shell -builtin-and-Shell-keyword

  • <

  • &&および||

    • [[ a = a && b = b ]]:true、論理and
    • [ a = a && b = b ]:構文エラー、&& ANDコマンド区切り文字として解析cmd1 && cmd2
    • [ a = a -a b = b ]:同等ですが、POSIX³で非推奨
    • [ a = a ] && [ b = b ]:POSIXおよび同等の信頼できる
  • (

    • [[ (a = a || a = b) && a = b ]]:false
    • [ ( a = a ) ]:構文エラー、()はサブシェルとして解釈されます
    • [ \( a = a -o a = b \) -a a = b ]:同等ですが、()はPOSIXで非推奨になりました
    • { [ a = a ] || [ a = b ]; } && [ a = b ] POSIX相当⁵
  • 展開時の単語分割とファイル名生成(split + glob)

    • x='a b'; [[ $x = 'a b' ]]:true、引用符は不要
    • x='a b'; [ $x = 'a b' ]:構文エラー、[ a b = 'a b' ]に展開
    • x='*'; [ $x = 'a b' ]:現在のディレクトリに複数のファイルがある場合の構文エラー。
    • x='a b'; [ "$x" = 'a b' ]:POSIXと同等
  • =

    • [[ ab = a? ]]:true パターンマッチング* ? [は魔法)を行うため。現在のディレクトリ内のファイルに展開しません。
    • [ ab = a? ]a? globが展開されます。したがって、現在のディレクトリ内のファイルに応じて、trueまたはfalseになる場合があります。
    • [ ab = a\? ]:glob展開ではなくfalse
    • ===[[[の両方で同じですが、==はBash拡張機能です。
    • case ab in (a?) echo match; esac:POSIXと同等
    • [[ ab =~ 'ab?' ]]:false⁴、''で魔法を失います
    • [[ ab? =~ 'ab?' ]]:true
  • =~

    • [[ ab =~ ab? ]]:true、POSIX 拡張正規表現 一致、?は展開されません
    • [ a =~ a ]:構文エラー。同等のbashはありません。
    • printf 'ab\n' | grep -Eq 'ab?':POSIX同等(単一行データのみ)
    • awk 'BEGIN{exit !(ARGV[1] ~ ARGV[2])}' ab 'ab?':POSIXと同等。

推奨事項:常に[]を使用します。

私が見たすべての[[ ]]コンストラクトに対応するPOSIXがあります。

[[ ]]を使用する場合:

  • 移植性を失う
  • 読者に別のbash拡張機能の複雑さを学習させる。 [は、奇妙な名前を持つ通常のコマンドであり、特別なセマンティクスは含まれていません。

¹Kornシェルの同等の[[...]]コンストラクトに触発された

²しかし、aまたはb+またはindexなど)の一部の値で失敗し、abが似ている場合は数値比較を行います。 10進整数。 expr "x$a" '<' "x$b"は両方を回避します。

³また、!(のようなaまたはbの一部の値でも失敗します。

bash 3.2以降で提供され、bash 3.1との互換性が提供されていない(BASH_COMPAT=3.1など)

⁵ただし、グループ化(ここでは、不要なサブシェルを実行する{...;}の代わりに(...)コマンドグループ)は、||および&&シェル演算子としては必要ありません(反対に) ||演算子と&&[[...]]演算子、または-o/-a[演算子)の優先順位は同じです。したがって、[ a = a ] || [ a = b ] && [ a = b ]は同等です。

[[ ]]にはもっと多くの機能があります - 詳細については Advanced Bash Scripting Guide をご覧ください。特に拡張テストコマンド第7章テスト のセクション。

ちなみに、ガイドノートとして、[[ ]]がksh88(1988年版のKornシェル)で導入されました。

52
anon

から どのコンパレータ、テスト、かっこ、または二重かっこ、最速?http://bashcurescancer.com

二重括弧はテストとしての単一括弧がシェル組み込みコマンドである(そして実際には同じコマンドである)「複合コマンド」です。したがって、シングルブラケットとダブルブラケットは異なるコードを実行します。

Testとシングルブラケットは、別々の外部コマンドとして存在するため、最も移植性があります。ただし、リモートの最新バージョンのBASHを使用している場合は、二重括弧がサポートされています。

8
f3lix

[[を使えない典型的な状況はautotoolsのconfigure.acスクリプトにあります。大括弧には特別な意味があります。したがって、[または[[の代わりにtestを使用する必要があります。testと[は同じです。プログラム。

次のことに興味がある場合 Googleのスタイルガイド

Test、[および[[

[[...]]は、[[and]]と[[...]]の間でパス名の展開やワード分割が行われないため、エラーを削減します。

# This ensures the string on the left is made up of characters in the
# alnum character class followed by the string name.
# Note that the RHS should not be quoted here.
# For the gory details, see
# E14 at https://tiswww.case.edu/php/chet/bash/FAQ
if [[ "filename" =~ ^[[:alnum:]]+name ]]; then
  echo "Match"
fi

# This matches the exact pattern "f*" (Does not match in this case)
if [[ "filename" == "f*" ]]; then
  echo "Match"
fi

# This gives a "too many arguments" error as f* is expanded to the
# contents of the current directory
if [ "filename" == f* ]; then
  echo "Match"
fi
1
crizCraig