web-dev-qa-db-ja.com

Bash演算子の違いは何ですか[[vs [vs(vs((?

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

[[ , [ , ( , ((

私は人々がこのようなif文でそれらを使用するのを見ました:

if [[condition]]

if [condition]

if ((condition))

if (condition)
298
RetroCode

ifステートメントは通常、次のようになります。

_if commands1
then
   commands2
else
   commands3
fi
_

_commands1_の終了コードがゼロの場合、then句が実行されます。終了コードがゼロ以外の場合、else句が実行されます。 _commands1_は単純または複雑です。たとえば、演算子_;_、_&_、_&&_、または_||_のいずれかで区切られた1つ以上のパイプラインのシーケンスにすることができます。以下に示すif条件は、_commands1_の特殊なケースです。

  1. _if [ condition ]_

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

  2. _if [[ condition ]]_

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

  3. if ((condition))

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

  4. if (command)

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

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

  5. _if command_

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

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

シェルでは、everyコマンドは条件付きコマンドです。すべてのコマンドの戻りステータスは、成功を示す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 …
    _
  • Bash/ksh/zshで、x(数値と見なされます)の値が2以上かどうかをテストします。

    _if ((x >= 2)); then …
    _

bashのドキュメント から:

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

言い換えると、「cd」のように「リスト」で発生したことが、_(_および_)_の外では効果がないことを確認します。リークするのは、最後のコマンドの終了コード、またはエラーを生成する最初のコマンド__set -e_(ifwhileなどのいくつかのコマンド以外)だけです。

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

これは、数学を行うためのbash拡張機能です。これは、exprのすべての制限なしにexprを使用するのと多少似ています(至る所にスペースがある、_*_をエスケープするなど)。

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

[[と一緒に使用すると、<および>演算子は、現在のロケールを使用して辞書式順序で並べ替えます。

これは、testが提供するものと少し似ていますが、より強力な、文字列、数値、およびファイルを比較する高度なテストを提供します。

_[ expr ]_条件式exprの評価に応じて、0(true)または1(false)のステータスを返します。各演算子と演算子は別々の引数でなければなりません。式は、上記の条件式で説明したプライマリで構成されます。 testはオプションを受け入れません。また、オプションの終わりを示す-の引数を受け入れて無視しません。

[...]

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

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

20
Alexis Wilke

_[_ vs _[[_

この回答では、質問の_[_と_[[_のサブセットについて説明します。

Bash 4.3.11のいくつかの違い:

  • POSIX対Bash拡張:

  • 通常のコマンド対魔法

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

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

      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、論理および
    • _[ 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 ]_同等のPOSIX5
  • 拡張時の単語分割とファイル名生成(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、glob拡張ではない
    • _=_と_==_は、_[_と_[[_の両方で同じですが、_==_はBash拡張機能です。
    • case ab in (a?) echo match; esac:同等のPOSIX
    • _[[ ab =~ 'ab?' ]]_:false4、_''_で魔法を失う
    • _[[ 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シェルの同等の_[[...]]_構成から発想を得た

²しかし、aまたはbの一部の値(_+_またはindexなど)で失敗し、aおよびbは、10進整数のように見えます。 _expr "x$a" '<' "x$b"_は両方を回避します。

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

4 bash 3.2以降では、bash 3.1との互換性が提供されていません(_BASH_COMPAT=3.1_など)。

5 ただし、_{...;}_および_(...)_シェル演算子(反対ではない)として、グループ化(ここでは、_||_ではなく_&&_コマンドグループを使用)は不要です。 _||_および_&&_ _[[...]]_演算子または_-o_/_-a_ _[_演算子)の優先順位は同じです。したがって、_[ a = a ] || [ a = b ] && [ a = b ]_は同等です。

いくつかの例:

伝統的なテスト:

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

testおよび[は他のコマンドと同様に、引用符で囲まれていない限り、変数は単語に分割されます。

新しいスタイルのテスト

[[ ... ]]は(新しい)特別なShell構文であり、動作が少し異なります。最も明白なことは、Word変数を分割しないことです。

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 ...
13
ilkkachu

グループ化コマンド

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,ltおよびgtを使用する必要があります。

拡張ブラケットつまり[[]]

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

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

  • [はテストコマンドの同義語です。シェルに組み込まれていても、新しいプロセスを作成します。
  • [[は、その改良版であり、プログラムではなくキーワードです。
  • [[KornおよびBashによって理解されます。

ソース

1
Premraj