bash [[]]コマンド内の自動変数展開


13

の変数を間接参照する場合、符号bashを使用する必要があります$。それにもかかわらず、次のようにうまく機能しているようです:

x=5
[[ x -gt 2 ]]

誰もこれを説明できますか?

編集:(詳細)

私が意味するのは、[[]]コマンドが$記号なしで変数xを逆参照する方法と理由です。はい、x = 1の場合、ステートメントはfalseと評価されます(ステータス1を返します)


2
「うまく働く」とはどういう意味ですか?そしてあなたがx=1従うとあなたの評価は変わります[[ x -gt 2]]か?
nohillside

つまり、[[]]コマンドが$記号なしで変数xを逆参照している方法と理由です。はい、x = 1の場合、ステートメントはfalse(ステータス1を返す)
ゲスト

回答:


9

その理由は-eq、引数の算術評価が強制されるためです。

算術演算子:-eq-gt-lt-ge-leおよび-ne内部[[ ]](kshので、zshのおよびBash)自動的に導く必要がC言語のような変数名を展開するための手段$

  • 確認のために、bashのソースコードを調べる必要があります。マニュアルでは直接確認することはできません。

    test.c算術演算子の処理の中では、この関数に分類されます。

    arithcomp (s, t, op, flags)

    どこst両方のオペランドです。オペランドはこの関数に渡されます。

    l = evalexp (s, &expok);
    r = evalexp (t, &expok);

    この関数evalexpは、次のexpr.cヘッダーを持つ内部で定義されています。

    /* expr.c -- arithmetic expression evaluation. */

    したがって、はい、算術演算子の両側は算術式の評価に(直接)該当します。直接、no buts、no ifs。


実際には:

 $ x=3

両方とも失敗します:

 $ [[ x = 4 ]] && echo yes || echo no
 no

 $ [[ x = 3 ]] && echo yes || echo no
 no

どちらが正しいか、x展開されてxおらず、数値と等しくない。

しかしながら:

 $ [[ x -eq 3 ]] && echo yes || echo no
 yes

 $ [[ x -eq 4 ]] && echo yes || echo no
 no

指定された変数xは展開されます($がなくても)。

これは[…]、zshまたはbashで発生しません(kshで発生します)。


それはaの中で起こることと同じ$((…))です:

 $ echo $(( x + 7 ))
 10

そして、これは(非常に)再帰的であることを理解してください(ダッシュとyashを除く):

 $ a=b b=c c=d d=e e=f f=3
 $ echo "$(( a + 7 ))" 
 10

😮

そして非常に危険です:

 $ x='a[$(date -u)]'
 $ [[ x -eq 3 ]] && echo yes || echo no
 bash: Tue Dec  3 23:18:19 UTC 2018: syntax error in expression (error token is "Dec  3 23:18:19 UTC 2018")

構文エラーは簡単に回避できます。

 $ a=3; x='a[$(date -u >/dev/tty; echo 0)]'

 $ [[ x -eq 3 ]] && echo yes || echo no
 Tue Dec  4 09:02:06 UTC 2018
 yes

sayingにあるように:入力をサニタイズする

 $ [[ ${x//[^0-9]} -eq 3 ]] && echo yes || echo no
 no

😮の終わり


(古い)外部/usr/bin/test(ビルトインではないtest)とさらに古い外部の両方は、式を整数のみ(および明らかに10進整数のみ)expr展開しません。

 $ /usr/bin/test "x" -eq 3
 /usr/bin/test: invalid integer x

 $ expr x + 3
 expr: non-integer argument

面白い。これがどのように可能であるかを伝えるのは難しくありません- [[キーワードであるため、演算子とオペランドは、コマンドが展開された後ではなく読み込まれたときに検出されます。したがって、例えば、よりスマートな方法で[[扱うことができます。しかし、私が疑問に思うのは、複合コマンドを解釈するためにbashが使用するロジックに関するドキュメントはどこにありますか?それは私には非常に明白に見えないし、私は満足な説明を見つけることが明らかにできないんですか。-eq[maninfo bash
FRAさん

Bashは、これを私が見つけることができるどこにも文書化しません。man ksh93に一種の説明があります。次の廃止された算術比較も許可されています:exp1 -eq exp2。このテキストがある人zshbuiltinsのセクション算術演算子は、むしろ算術式よりも引数の整数を期待します。これは、一部の引数が、この引用符で指定されていない条件の下で、組み込みテストによって算術式として扱われることを確認します。私はソースコードで確認します…....test
Isaac

7

数値比較のオペランドは-eq-gt-lt-ge-leおよび-ne演算式として取られます。いくつかの制限はありますが、それらは単一のシェルワードである必要があります。

算術式での変数名の動作は、Shell Arithmeticで説明されています。

シェル変数はオペランドとして使用できます。式が評価される前に、パラメータ展開が実行されます。式内では、パラメータ拡張構文を使用せずにシェル変数を名前で参照することもできます。nullまたは未設定のシェル変数は、パラメーター拡張構文を使用せずに名前で参照すると0と評価されます。

また:

変数の値は、参照されると算術式として評価されます

しかし、数値比較には算術式を使用すると言われているドキュメントの部分は実際には見つかりません。以下の条件付きコンストラクトでは説明されておらず[[Bash条件式でも説明されていません。

しかし、実験では、上記のように機能するようです。

したがって、このようなものは動作します:

a=6
[[ a -eq 6 ]] && echo y 
[[ 1+2+3 -eq 6 ]] && echo y
[[ "1 + 2 + 3" -eq 6 ]] && echo y

これも(変数の値が評価されます):

b='1 + 2 + 3'
[[ b -eq 6 ]] && echo y

しかし、これはそうではありません。[[ .. ]]が解析されるとき、単一のシェルワードではないため、条件に構文エラーがあります。

[[ 1 + 2 + 3 -eq 6 ]] && echo y

他の算術コンテキストでは、式が空白なしである必要はありません。これは999、角かっこがインデックス内の算術式を明確に区切るため、を出力します。

a[6]=999; echo ${a[1 + 2 + 3]}

一方、=比較はパターンマッチであり、算術や算術コンテキストで行われる自動変数展開は含まれません(条件付き構成):

場合==!=演算子が使用されているextglobシェルオプションが有効になったかのように、オペレータの右側の文字列は、パターンマッチングに説明ルールに従ってパターンとみなされ、一致しています。=オペレータは同じです==

文字列が明らかに異なるため、これはfalseです。

[[ "1 + 2 + 3" = 6 ]] 

数値は同じですが、これは次のとおりです。

[[ 6 = 06 ]] 

ここでも、文字列(x6)が比較されますが、それらは異なります:

x=6
[[ x = 6 ]]

ただし、これにより変数が展開されるため、次のようになります。

x=6
[[ $x = 6 ]]

数値比較が算術式を取ると言われているドキュメントの部分を実際に見つけることができません。確認はしているコード
アイザック

最も近いのは、の説明でarg1 OP arg2は、引数は正または負の整数である可能性があるということです。これは、算術式として扱われることを意味すると思われます。紛らわしいことに、これはゼロにできないことも意味します。:)
バーマー

@バーマー、えー、そう。しかし、それは数値比較に[も当てはまり、それらは算術式ではありません。代わりに、Bashは非整数について文句を言います。
イルカチュウ

@ilkkachu [は外部コマンドであり、シェル変数にアクセスできません。多くの場合、組み込みコマンドで最適化されますが、それでも同じように動作します。
バーマー

@Barmar、私が意味したのは、「Arg1とarg2は正または負の整数である可能性がある」というフレーズです。表示されますバッシュ条件式、及びそのリストは以下の製品に適用さ[と全く同じように[[。であっても、[to -eqおよびfriends のオペランドは整数である必要があります。そのため、説明も適用されます。「算術式として解釈される」ことを意味する「整数でなければならない」とすることは、どちらの場合にも当てはまりません。(おそらく、少なくとも部分的に[は、あなたが言うように通常のコマンドのように振る舞うためです。)
ilkkachu

1

はい、あなたの観察は正しいです、変数の展開は二重括弧の下の式で実行される[[ ]]ので$、変数名の前に置く必要はありません。

これは、bashマニュアルに明示的に記載されています。

[[expression]]

(...)単語の分割とパス名の展開は、[[と]]の間の単語では実行されません。チルダ展開、パラメータおよび変数展開、算術展開、コマンド置換、プロセス置換、および引用削除が実行されます。

これは、シングルブラケットバージョンの場合ではないことを通知[ ]として、[シェルキーワード(構文)ではなく、コマンド(bashで、それは他のシェルは、外部使用することができ、組み込みであり、テストに並びます)。


1
返信いただきありがとうございます。これは数字に対してのみ機能しているようです。x = city [[$ x == city]]これは、$記号なしでは機能しません。
ゲスト

3
ここにもっとあるように見えます:(x=1; [[ $x = 1 ]]; echo $?)Returns 0(x=1; [[ x = 1 ]]; echo $?)returns 1、つまり、x文字列を比較するときにパラメータ展開は実行されません。この動作は、算術展開によってトリガーされる算術評価のように見え(x=1; echo $((x+1)))ます。(算術評価についてman bashは、「式内では、パラメーター拡張構文を使用せずにシェル変数を名前で参照することもできます」と述べています。
fra-san

@ fra-san確かに、-gt演算子は数値を想定しているため、内部全体のように式全体が再評価され、(())一方で==文字列を想定している代わりにパターンマッチング関数がトリガーされます。私はソースコードを掘り下げたわけではありませんが、理にかなっています。
jimmij

[bashに組み込まれたシェルです。
ニザムモハメッド

1
@NizamMohamedこれは組み込みですが、それでもキーワードではありません。
クサラナナンダ
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.