Bash演算子[[vs [vs(vs((?


246

これらの演算子がbash(括弧、二重括弧、括弧、二重括弧)で使用された場合に異なる動作をすることについて少し混乱しています。

[[ , [ , ( , ((

私は人々がこのようなifステートメントでそれらを使用するのを見てきました:

if [[condition]]

if [condition]

if ((condition))

if (condition)



3
@cuonglm Ironicは、このリンクが最初の結果としてこの質問を生成するためです。逆説!
非常識な

5
ドキュメントを読むことはオプションではないと思いますか?
軌道上の明るさのレース

34
括弧や括弧はドキュメントで検索するのはそれほど簡単ではありません。これらの機能の名前がわからない場合は、これで十分です。
-ilkkachu

回答:


258

if文は、通常のようになります。

if commands1
then
   commands2
else
   commands3
fi

then終了コードがあれば句が実行されcommands1、ゼロです。終了コードがゼロ以外の場合、else句が実行されます。 commands1シンプルでも複雑でもかまいません。これは、例えば、オペレータの一つで区切られた1つの以上のパイプラインの配列とすることができる;&&&、または||if以下に示す条件は、単なる特殊なケースですcommands1

  1. if [ condition ]

    これは従来のシェルtestコマンドです。すべてのPOSIXシェルで使用可能です。テストコマンドは終了コードを設定し、ifステートメントはそれに応じて動作します。典型的なテストは、ファイルが存在するか、ある数値が別の数値と等しいかどうかです。

  2. if [[ condition ]]

    これは、上の新しいアップグレードバリエーションですtestからkshのはbashzshのがもサポートしています。このtestコマンドは終了コードも設定し、ifステートメントはそれに応じて動作します。拡張機能の中で、文字列が正規表現に一致するかどうかをテストできます。

  3. if ((condition))

    bashzshもサポートする別のksh拡張。これは算術演算を実行します。算術の結果として、終了コードが設定され、ステートメントはそれに応じて動作します。算術計算の結果がゼロ以外の場合、ゼロ(true)の終了コードを返します。のように、このフォームはPOSIXではないため、移植できません。if[[...]]

  4. if (command)

    これにより、サブシェルでコマンドが実行されます。コマンドが完了すると、終了コードが設定され、ifステートメントはそれに応じて動作します。

    このようなサブシェルを使用する一般的な理由はcommandcommand必要に応じて変数の割り当てやシェルの環境に対する他の変更の副作用を制限するためです。このような変更は、サブシェルが完了した後も残りません。

  5. if command

    コマンドが実行され、ifステートメントは終了コードに従って動作します。


24
5番目のオプションを含めてくれてありがとう。これは、これが実際にどのように機能するかを理解するための鍵であり、驚くほど十分に活用されていません。

4
[は、実際にはバイナリであり、内部コマンドまたはシンボルではないことに注意してください。一般的に住んでい/binます。
ジュリアンR.

8
@JulienR。実際に[はビルトインですtest。互換性の理由で利用可能なバイナリバージョンがあります。チェックアウトhelp [help testます。
オールドタイマー

4
しばらく((POSIXイマイチ、ということは注目に値する$((。すなわち算術展開があり、それらを混同するのは簡単です多くの場合、問題を回避するには、のようなものを使用することです[ $((2+2)) -eq 4 ]conditinal文で算術を利用するために
Sergiy Kolodyazhnyy

1
この回答に複数回投票できるといいのですが。完全な説明。
アンソニー・ガトリン

77
  • (…)括弧はサブシェルを示します。それらの中にあるのは、他の多くの言語のような表現ではありません。これはコマンドのリストです(括弧の外側と同じです)。これらのコマンドは個別のサブプロセスで実行されるため、括弧内で実行されるリダイレクト、割り当てなどは、括弧外では効果がありません。
    • 先頭にドル記号$(…)が付いているのはコマンド置換です:括弧内にコマンドがあり、コマンドからの出力はコマンド行の一部として使用されます(置換が二重引用符で囲まれていない限り、追加の展開の後、それは別の話です) 。
  • { … }中括弧は、コマンドをグループ化するという点では括弧のようなものですが、グループ化ではなく解析のみに影響します。プログラムx=2; { x=4; }; echo $xは4をx=2; (x=4); echo $x出力しますが、2を出力します(キーワードである中括弧も区切られ、コマンド位置(したがって{;前後のスペース})にある必要がありますが、括弧はありません。これは単なる構文の癖です)。
    • 先頭にドル記号${VAR}が付いているのは、パラメータの拡張であり、変数の値に拡張され、さらに変換が行われる可能性があります。ksh93シェルもサポートしています${ cmd;}サブシェルを起動しないコマンド置換の形として。
  • ((…))二重括弧は算術命令、つまり整数の計算を囲み、構文は他のプログラミング言語に似ています。この構文は、主に割り当てと条件で使用されます。これはksh / bash / zshにのみ存在し、単純なshには存在しません。
    • 算術式$((…))でも同じ構文が使用され、式の整数値に展開されます。
  • [ … ]単一の括弧が条件式を囲みます。条件式は主に、変数が空かどうかをテストしたり、ファイルが存在するかどうかをテストするなどの演算子に基づいて構築されます。各演算子の周りにスペースが必要であることに注意してください(たとえば、not )、および角括弧の内側と外側の両方にスペースまたは文字(たとえば、not )が必要です。-n "$variable"-e "$file"[ "$x" = "$y" ][ "$x"="$y" ];[ -n "$foo" ][-n "$foo"]
  • [[ … ]]二重括弧は、いくつかの追加機能を備えたksh / bash / zshの条件式の代替形式です。たとえば[[ -L $file && -f $file ]]、ファイルが通常のファイルへのシンボリックリンクであるかどうかをテストするために書くことができます[ -L "$file" ] && [ -f "$file" ]。参照してください引用符スペースでパラメータ展開は二重括弧[[ではなく、単一のブラケットの内部に働くのはなぜ?このトピックの詳細については。

シェルでは、すべてのコマンドは条件付きコマンドです。すべてのコマンドの戻りステータスは、成功を示す0または失敗を示す1〜255の整数(および場合によってはそれ以上)です。[ … ]コマンド(または[[ … ]]構文形式)も綴らすることができる特定のコマンドでありtest …、ファイルが存在する場合に成功し、または文字列が空でない場合、または数が他よりも小さい場合、等((…))場合構文形式は成功数ゼロ以外です。シェルスクリプトの条件の例を次に示します。

  • myfile文字列が含まれているかどうかをテストしますhello

    if grep -q hello myfile; then 
  • mydirがディレクトリの場合は、そのディレクトリに移動して次のことを行います。

    if cd mydir; then
      echo "Creating mydir/myfile"
      echo 'some content' >myfile
    else
      echo >&2 "Fatal error. This script requires mydir to exist."
    fi
  • myfile現在のディレクトリに呼び出されるファイルがあるかどうかをテストします。

    if [ -e myfile ]; then 
  • 同じですが、ぶら下がるシンボリックリンクも含まれます。

    if [ -e myfile ] || [ -L myfile ]; then 
  • x(数値と想定される)の値が少なくとも2であるかどうかをテストします

    if [ "$x" -ge 2 ]; then 
  • xbash / ksh / zsh で、(数値と見なされる)の値が少なくとも2であるかどうかをテストします。

    if ((x >= 2)); then 

シングルブラケットがサポートしていることに注意してください-a代わりのを&&1に書くことができますので、: [ -L $file -a -f $file ]、余分ななし括弧内の文字の数が同じである[]...
アレクシス・ヴィルケ

6
@AlexisWilke演算子-aおよび-oは、関係するオペランドの一部が演算子のように見える場合、不正な解析につながる可能性があるため、問題があります。だから私はそれらに言及しない:彼らはゼロの利点を持ち、常に機能するとは限らない。そして、正当な理由なしに引用符で囲まれていない変数展開を決して書かないでください:[[ -L $file -a -f $file ]]結構ですが、必要な単一の括弧を使用します[ -L "$file" -a -f "$file" ]$file常に/orで始まる場合は大丈夫です./)。
ジル

[[ -L $file && -f $file ]](バリアント-aではない[[...]])ことに注意してください。
ステファンシャゼラス

18

bashドキュメントから:

(list)listはサブシェル環境で実行されます(下記のコマンド実行環境を参照)。シェルの環境に影響を与える変数の割り当てと組み込みコマンドは、コマンドの完了後は有効になりません。戻りステータスはリストの終了ステータスです。

言い換えれば、「リスト」(aなどcd)で発生することは、(and 以外では効果がないことを確認します)。漏れる唯一のものは、最後のコマンドまたはと終了コードであるset -e(例えば、数以外のエラー発生する第1のコマンドifwhile等)

((expression))式は、以下の算術評価で説明されている規則に従って評価されます。式の値がゼロ以外の場合、戻りステータスは0です。それ以外の場合、戻りステータスは1です。これはlet "expression"とまったく同じです。

これは、数学を実行できるbash拡張機能です。これはexpr、すべての制限なしで使用することに多少似てexprいます(どこにでもスペースを入れる、エスケープする*など)

[[ expression ]]条件式expressionの評価に応じて、0または1のステータスを返します。式は、以下の条件式で説明するプライマリで構成されます。単語の分割とパス名の展開は、[[と]]の間の単語では実行されません。チルダ展開、パラメータおよび変数展開、算術展開、コマンド置換、プロセス置換、および引用削除が実行されます。-fなどの条件演算子は、プライマリとして認識されるように引用符で囲まないでください。

[[と組み合わせて使用​​すると、<および>演算子は現在のロケールを使用して辞書式にソートされます。

これにより、文字列、数字、およびファイルを比較するための高度なテストがtest提供されますが、より強力です。

[ expr ]条件式exprの評価に応じて、0(true)または1(false)のステータスを返します。各演算子と演算子は個別の引数である必要があります。式は、条件式で前述したプライマリで構成されます。testはオプションを受け入れず、オプションの終わりを示す-の引数も受け入れて無視しません。

[...]

これはを呼び出しますtest。実際、昔[は、へのシンボリックリンクでしたtest。同じように機能し、同じ制限があります。バイナリは起動された名前を知っているため、テストプログラムはパラメータを見つけるまでパラメータを解析できます]。楽しいUnixトリック。

bash[およびの場合はtest(コメントで述べたように)組み込み関数ですが、ほぼ同じ制限が適用されます。


1
けれどもtestそして[もちろんバッシュで組み込みコマンドですが、それは外部のバイナリがあまりにも存在している可能性があります。
-ilkkachu

1
の外部バイナリ[test、ほとんどの最新システムへのシンボリックリンクではありません。
Random832

1
どういうわけか、それらを組み合わせていくつかの条件を追加するのではなく、必要なものを正確に持っている2つの別々のバイナリを作成するのが面倒だと思います。実際にstrings /usr/bin/testはヘルプテキストも表示されているので、何と言ったらいいのかわかりません。
-ilkkachu

2
@ Random832予期しないarg0の振る舞いを避けるためのGNUの理論的根拠についてはあなたの意見を聞きますが、POSIXの要件については、それほど肯定的ではありません。このtestコマンドは、標準ではスタンドアロンのファイルベースのコマンドとして存在する必要があることは明らかですが、その[バリアントもそのように実装する必要があることを示すものはありません。たとえば、Solaris 11は[実行可能ファイルを提供しませんが、それでもPOSIX標準に完全に準拠しています
-jlliagre

2
(出口1)括弧の外側に効果があります。
アレクサンダー

14

[[[

この回答は、質問の[vs [[サブセットをカバーします。

Bash 4.3.11のいくつかの違い:

  • POSIX対Bash拡張機能:

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

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

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

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

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

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

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

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

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

  • <

  • && そして ||

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

    • [[ (a = a || a = b) && a = b ]]:偽
    • [ ( a = a ) ]:構文エラー、()サブシェルとして解釈されます
    • [ \( a = a -o a = b \) -a a = b ]:同等()ですが、POSIXでは非推奨です
    • { [ a = a ] || [ a = b ]; } && [ a = b ]POSIXの同等品5
  • 単語分割と展開時のファイル名生成(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
    • =そして==、両方で同じである[[[、しかし==バッシュの拡張機能です。
    • case ab in (a?) echo match; esac:POSIXと同等
    • [[ ab =~ 'ab?' ]]:false 4、魔法を失う''
    • [[ ab? =~ 'ab?' ]]:true
  • =~

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

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

[[ ]]私が見たすべての構成体にPOSIXに相当するものがあります。

あなたを使用する[[ ]]場合:

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

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

²が、aor b+orなどindex)の一部の値では失敗し、10進整数の場合は数値比較をa行いbます。expr "x$a" '<' "x$b"両方で動作します。

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

bash 3.2以降の4および提供されているbash 3.1との互換性は有効ではありません(などBASH_COMPAT=3.1

5(とここでグルーピングものの{...;}代わりにコマンド群(...)として不要サブシェルを実行することになる)が必要でない||&&(とは対照的に、シェル演算子||及び&& [[...]]オペレータ又は-o/ -a [オペレータ)は等しい優先順位を有します。だから[ a = a ] || [ a = b ] && [ a = b ]同等になります。


@StéphaneChazelas情報ありがとう!expr答えに追加しました。「Bash拡張」という用語は、Bashが構文を追加する最初のシェルであったことを意味するものではありません。POSIXshとBashを学ぶだけで、私は夢中になります。
Ciro Santilli新疆改造中心法轮功六四事件

見る man testあなたが試みman [て迷子になったかどうか確認します。POSIXバリアントについて説明します。
ジョナサンコマー

13

いくつかの例:

従来のテスト:

foo="some thing"
# check if value of foo is not empty
if [ -n "$foo" ] ; then... 
if test -n "$foo" ; then... 

testそして[、それは引用符でいない限り、他のもののようなコマンドは、その変数が単語に分割されています。

新しいスタイルのテスト

[[ ... ]] (新しい)特殊なシェル構造であり、動作が少し異なります。最も明白なことは、変数をワード分割しないことです。

if [[ -n $foo ]] ; then... 

およびここにいくつかのドキュメント[[[

算術検定:

foo=12 bar=3
if (( $foo + $bar == 15 )) ; then ...  

「通常の」コマンド:

上記のすべてが通常のコマンドのように動作し、 if任意のコマンドでき。

# grep returns true if it finds something
if grep pattern file ; then ...

複数のコマンド:

または、複数のコマンドを使用できます。一連のコマンドをラップすると( ... )、サブシェルでコマンドが実行され、シェルの状態(作業ディレクトリ、変数)の一時的なコピーが作成されます。別のディレクトリで一時的にプログラムを実行する必要がある場合:

# this will move to $somedir only for the duration of the subshell 
if ( cd $somedir ; some_test ) ; then ...

# while here, the rest of the script will see the new working
# directory, even after the test
if cd $somedir ; some_test ; then ...

1

グループ化コマンド

Bashには、1つのユニットとして実行されるコマンドのリストをグループ化する2つの方法があります。

( list )コマンドのリストを括弧で囲むと、サブシェル環境が作成され、リスト内の各コマンドがそのサブシェルで実行されます。リストはサブシェルで実行されるため、変数の割り当てはサブシェルの完了後も有効になりません。

$ a=1; (a=2; echo "inside: a=$a"); echo "outside: a=$a"
inside: a=2
outside: a=1

{ list; }中括弧の間にコマンドのリストを配置すると、現在のシェルコンテキストでリストが実行されます。サブシェルは作成されません。リストに続くセミコロン(または改行)が必要です。ソース

${} Parameter expansion Ex:  ANIMAL=duck; echo One $ANIMAL, two ${ANIMAL}s
$() Command substitution Ex: result=$(COMMAND) 
$(()) Arithmetic expansion Ex: var=$(( 20 + 5 )) 

条件付きコンストラクト

シングルブラケットすなわち[]
比較のため==, !=, <,>して使用すべきであると数値比較のためにeq, ne,ltgt使用する必要があります。

拡張ブラケット ie[[]]

上記のすべての例で、条件式を囲むために単一のブラケットのみを使用しましたが、bashでは、単一ブラケット構文の拡張バージョンとして機能する二重ブラケットを使用できます。

比較のために==, !=, <,>文字通り使用できます。

  • [testコマンドの同義語です。シェルに組み込まれている場合でも、新しいプロセスを作成します。
  • [[ これは新しい改良バージョンであり、プログラムではなくキーワードです。
  • [[理解されるKornBash

ソース

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