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


574

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

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

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


19
時々自分がために、その深い説明:)としてを必要とせずにアドバイスを聞くことができるよう、柔軟で[[コードが良いと明確であることではなく、その日を覚えているときに、ポートよでないデフォルトのシェルを使用してシステム上のscriptworks bashksh、など[は醜くて扱いにくいですがAK-47、どんな状況でも機能します。
2013

178
@rook深い説明がなくてもアドバイスを聞くことができます。しかし、説明を要求しても理解できない場合は、通常は赤旗です。「信頼するが、検証する」など。
Josip Rodin

6
参照してください:Bashの事業者間の差は(対((VS [[[対何ですか? UnixのとLinux SEに。
codeforester

1
@rook言い換えると、「言われたことを実行し、質問をしない」
グリフ

@rook意味がありませんでした。
Rakib

回答:


607

[[驚きが少なく、一般的に安全に使用できます。ただし、移植性はありません。POSIXは何をするかを指定せず、一部のシェルのみがサポートします(bashのほかに、kshもサポートしていると聞きました)。たとえば、次のことができます

[[ -e $b ]]

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

[[=~には、Cライクな言語で知られているような演算子とともに正規表現のマッチングを行うなど、他にも優れた機能があります。:ここでは良いページはそれについてですテストの違いは何ですか、[[[およびBashテスト


21
最近はbashがどこにでもあることを考えると、かなりポータブルなものだと思う傾向があります。私の唯一の一般的な例外は、busyboxプラットフォーム上です-とにかく、それが実行されているハードウェアを考えると、とにかく特別な努力をしたいと思うでしょう。

14
@guns:確かに。2番目の文が最初の文を反証することをお勧めします。ソフトウェア開発全体を考えると、「bashはどこにでもある」と「例外はbusyboxプラットフォームです」は完全に互換性がありません。busyboxは組み込み開発で広く使われています。
オービットでの軽さのレース'08年

11
@guns:bashが私のボックスにインストールされている場合でも、スクリプトを実行していない可能性があります(FreeBSDとUbuntuの標準のように、/ bin / shがダッシュバリアントにシンボリックリンクされている可能性があります)。Busyboxには、さらに奇妙な点もあります。最近の標準のコンパイルオプションでは、解析さ[[ ]][ ]ますが、と同じ意味で解釈されます。
dubiousjim

59
@dubiousjim:(このような)bashのみの構成を使用する場合は、#!/ bin / shではなく#!/ bin / bashを使用する必要があります。
Nick Matteo

16
そのため、スクリプトを使用します#!/bin/shが、#!/bin/bashBASH固有の機能に依存したらすぐに使用に切り替え、Bourneシェルポータブルではなくなったことを示します。
アンソニー

151

行動の違い

Bash 4.3.11のいくつかの違い:

  • POSIX対Bash拡張:

  • 通常のコマンド対魔法

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

      ][それ以上の引数が使用されないようにするための単なる引数です。

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

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

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

    • [[ X ]]X魔法のように解析される単一の構造です。<&&||および()特別に処理し、単語分割ルールは異なるされています。

      とのような違いも=あり=~ます。

      バシェス語で[は:は組み込みコマンドであり[[、キーワードです:https : //askubuntu.com/questions/445749/whats-the-difference-between-shell-builtin-and-shell-keyword

  • <

  • && そして ||

    • [[ a = a && b = b ]]:true、論理的および
    • [ 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?グロブが拡張されます。したがって、現在のディレクトリ内のファイルに応じて、trueまたはfalseになります。
    • [ ab = a\? ]:false、グロブ拡張ではない
    • =そして==、両方で同じである[[[、しかし==バッシュの拡張機能です。
    • 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拡張機能の複雑さを学習させる。[奇妙な名前の通常のコマンドであり、特別なセマンティクスは含まれていません。

Ko [[...]]Kornシェルの同等の構成に触発

²しかし、いくつかの値のために失敗したab(よう+index)と数値の比較であれば行いab小数、整数のような表情を。expr "x$a" '<' "x$b"両方を回避します。

³ものいくつかの値のために失敗したabのような!、または(

b bash 3.2以降で提供され、bash 3.1との互換性が有効になっていません(などBASH_COMPAT=3.1)。

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


4
良い説明。[同じ理由で使用しています。
ゴードン

2
バシェ語では:) ha。違いを明確に説明し、POSIXに固執する理由。
ジョナサンコマー

56

[[ ]]より多くの機能があります。詳細については、「高度なBashスクリプトガイド」参照してください。具体的には、第7章の「テストコマンド拡張」セクションをご覧ください。

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


5
これはバシズムとはほど遠いもので、Kornシェルで最初に導入されました。
ヘンクランゲヴェルド

6
@トーマス、ABSは実際には多くのサークルで非常に貧弱な参照と見なされています。正確な情報は豊富にありますが、例に悪い習慣が見られないように注意を払うことはほとんどなく、維持されないまま多くの時間を費やしています。
Charles Duffy

@CharlesDuffyコメントありがとうございます。良い代替案を教えてください。私はシェルスクリプトの専門家ではありません。半年に1回程度スクリプトを書くために相談できるガイドを探しています。
トーマス

9
@ Thomas、Wooledge BashFAQおよび関連するwikiが私が使用しているものです。それらはFreenode #bashチャネルの住人によって積極的に維持されています(彼らは時々厄介なことですが、正確さについて深く気にかける傾向があります)。BashFAQ#31、mywiki.wooledge.org / BashFAQ / 031は、この質問に直接関連するページです。
Charles Duffy

13

どのコンパレータ、テスト、ブラケット、またはダブルブラケット、最速のでしょうか?http://bashcurescancer.com

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

テストと単一のブラケットは、独立した外部コマンドとして存在するため、最も移植性があります。ただし、リモートで最新バージョンのBASHを使用している場合は、二重ブラケットがサポートされます。


7
シェルスクリプトの最速の執着とは何ですか?私はそれを最もポータブルにしたいと思っており、改善[[がもたらすかもしれないことについてあまり気にすることができませんでした。しかし、その後、私は古い学校の古いおならです:-)
Jens

1
私は多くのシェルは、バージョンの組み込み証明だと思う[test外部バージョンも存在していても。
dubiousjim

4
@Jensは一般的に同意します。スクリプトの全体的な目的は(そうでしたか?)移植性です(そうでない場合、スクリプトではなくコードとコンパイルを行います)...考えられる2つの例外は次のとおりです:(1)タブ補完(ここで完了スクリプトは、多くの条件付きロジックにより非常に長くなる可能性があります); (2)スーパープロンプト(PS1=...crazy stuff...および/または$PROMPT_COMMAND)。これらについては、スクリプトの実行に目に見えるほどの遅延を望まない。
マイケル2013

1
最速の執着のいくつかは単にスタイルです。他のすべてが等しい場合、特にその構成が読みやすさも提供する場合は特に、より効率的なコード構成をデフォルトスタイルに組み込んでみませんか?移植性に関する限り、bashが適している多くのタスクは本質的に移植性がありません。たとえば、apt-get update前回の実行からX時間以上経過している場合は実行する必要があります。コードの制約のリストが長すぎて移植性がなくなると、非常に安心です。
Ron Burk、2017年

5

あなたがGoogleのスタイルガイドに従うことに夢中なら:

テスト、[および[[

[[ ... ]]なしパス名展開としてエラーを低減または単語分割が間に起こる[[]]し、[[ ... ]]正規表現のマッチングを可能にし[ ... ]ません。

# 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
グーグルは「パス名の展開は行われません...が行われました」と書いても[[ -d ~ ]]trueを返します(これ~はに展開されたことを意味します/home/user)。グーグルはもっと正確に書いているはずだと思う。
JamesThomasMoon1979

2

使用できない一般的な状況は[[autotools configure.acスクリプトです。角かっこには特別な異なる意味があるためtest[orの代わりに使用する必要があります[[-テストと[同じプログラムであることに注意してください。


2
autotoolsがPOSIXシェルではない場合、なぜ[POSIXシェル関数として定義されると予想するのですか?
ゴードン

autoconfスクリプトはシェルスクリプトのように見え、シェルスクリプトを生成し、ほとんどのシェルコマンドはその内部で動作します。
vy32

-1

[[]]二重括弧は特定のバージョンのSunOSではサポートされておらず、関数宣言内では完全にサポートされていません:GNU bash、バージョン2.02.0(1)-release(sparc-sun-solaris2.6)


1
非常に真実であり、決して重要ではありません。古いバージョン間でのbashの移植性を考慮する必要があります。人々は「bashはユビキタスで移植性がありますが、おそらく(難解なOSをここに挿入)」と言いますが、私の経験では、solarisは、移植性に特別な注意を払う必要があるプラットフォームの1つです。新しいOS、アレイ/関数などの問題/バグ。しかし、tr、sed、awk、tarのようなユーティリティ(スクリプトで使用される)でさえも、回避する必要のあるソラリスの奇妙な点と特異性があります。
マイケル

2
あなたは正解です... SolarisにはPOSIX以外のユーティリティがたくさんあります。「df」の出力と引数を見てください... SunのShame。うまくいけば、少しずつ消えていきます(カナダを除く)。
スカベンジャー

7
Solaris 2.6は真剣に?それは1997年にリリースされ、2006年にサポートを終了しました。それをまだ使用している場合、他の問題があると思います!。ちなみにそれは二重括弧を導入したものであるBash v2.02を使用したので、それと同じくらい古いものでも動作するはずです。2005年のSolaris 10はBash 3.2.51を使用し、2011年のSolaris 11はBash 4.1.11を使用しています。
peterh 2013年

1
スクリプトの移植性について。Linuxシステムでは、通常、各ツールのエディションのみが存在し、それがGNUエディションです。Solarisでは、通常、SolarisネイティブエディションまたはGNUエディション(たとえば、Solaris tarとGNU tar)から選択できます。GNU固有の拡張機能に依存している場合は、移植可能にするためにスクリプトでそれを記述する必要があります。Solarisでは、GNU grepが必要な場合、「g」を接頭辞として使用して、たとえば「ggrep」を使用します。
peterh 2013年

-2

簡単に言えば、[[は別のプロセスをフォークしないため、より優れています。ブラケットがないか単一のブラケットは、別のプロセスをフォークするため、二重ブラケットよりも低速です。


8
Testと[は、bashの同じ組み込みコマンドの名前です。を使用type [してこれを確認してください。
AB

1
@alberge、それは本当ですが[[、とは異なり[、bashコマンドラインインタープリターによって解釈される構文です。bashで入力してみてくださいtype [[。unix4linuxは正解です。古典的なBourneシェル[テストは新しいプロセスを分岐して真理値を決定しますが、[[構文(bash、zshなどによってkshから借用)はそうではありません。
Tim Gilbert、

2
@ティム、どちらのBourneシェルについて話しているのかはわかりません[が、BashおよびDash(/bin/shDebian派生のすべてのLinuxディストリビューション)に組み込まれています。
AB

1
ああ、そうですね、そうですね。古いSolarisまたはHP / UXシステムでは/ bin / shのようなものを考えていましたが、もちろん、互換性が必要な場合は、[[も使用しません。
Tim Gilbert、

1
@alberge BourneシェルはBashではありません(別名Bourne Again SHell)。
kiamlaluno 2017
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.