web-dev-qa-db-ja.com

`[`が組み込みシェルであり、 `[[`がシェルキーワードであるのはなぜですか?

私の知る限り、[[[の拡張バージョンですが、[[をキーワードとして表示し、[が組み込みとして表示されると混乱します。

[root@server ~]# type [
[ is a Shell builtin
[root@server ~]# type [[
[[ is a Shell keyword

[〜#〜] tldp [〜#〜]は言います

ビルトインは同じ名前のシステムコマンドの同義語である場合がありますが、Bashはそれを内部で再実装します。たとえば、Bash echoコマンドは/ bin/echoと同じではありませんが、動作はほとんど同じです。

そして

キーワードは、予約済みの単語、トークン、または演算子です。キーワードはシェルにとって特別な意味を持ち、実際にシェルの構文の構成要素です。例として、for、while、do、および!キーワードです。ビルトインと同様に、キーワードはBashにハードコードされていますが、ビルトインとは異なり、キーワード自体はコマンドではなく、コマンド構造のサブユニットです。 [2]

[[[の両方をキーワードにすべきではないでしょうか。ここで欠けているものはありますか?また、 このリンク は、[[[の両方が同じ種類に属している必要があることを再確認します。

68
Sree

[[[の違いは非常に重要です。

  • [はコマンドです。その引数は、他のコマンド引数が処理されるのと同じ方法で処理されます。たとえば、次のことを考慮してください。

    [ -z $name ]
    

    シェルは$nameを展開し、他のコマンドと同様に、結果に対してboth単語分割とファイル名生成を実行します。

    例として、以下は失敗します:

    $ name="here and there"
    $ [ -n $name ] && echo not empty
    bash: [: too many arguments
    

    これを正しく機能させるには、引用符が必要です。

    $ [ -n "$name" ] && echo not empty
    not empty
    
  • [[はシェルキーワードであり、その引数は特別なルールに従って処理されます。たとえば、次のことを考慮してください。

    [[ -z $name ]]
    

    シェルは$nameを展開しますが、他のコマンドとは異なり、結果に対してどちらも実行しません単語分割もファイル名生成も行いません。たとえば、nameにスペースが埋め込まれていても、次は成功します。

    $ name="here and there"
    $ [[ -n $name ]] && echo not empty
    not empty
    

概要

[はコマンドであり、シェルが実行する他のすべてのコマンドと同じ規則に従います。

ただし、[[はコマンドではなくキーワードなので、シェルはそれを特別に扱い、非常に異なるルールで動作します。

84
John1024

V7 Unix では、Bourne Shellがデビューしました— [testと呼ばれ、/bin/testとしてのみ存在していました。だから、あなたが今日書くコードは:

if [ "$foo" = "bar" ] ; then ...

代わりに次のように書きます

if test "$foo" = "bar" ; then ...

この2番目の表記はまだ存在しており、何が起こっているかがより明確であることがわかります。testというコマンドを呼び出して、その引数を評価し、ifが使用する exitステータスコード を返します次に何をするかを決定します。そのコマンドはシェルに組み込まれている場合と、外部プログラムの場合があります。

testの代替としての[は後で登場しました。²これはtestの組み込み同義語である可能性がありますが、組み込みのシェルを持たない最近のシステムでは/bin/[としても提供されます。

[およびtestは、同じコードを使用して実装できます。これは、OS Xの/bin/[/bin/testの場合で、これらは同じ実行可能ファイルへの ハードリンク です。³結果として、実装は末尾の]を完全に無視します。 /bin/[として呼び出す場合は必須であり、do/bin/testに提供しても問題はありません。⁴

[[と呼ばれる原始的なプログラムは存在しなかったため、その履歴は[[に影響を与えません。それは、 POSIX Shell への拡張としてそれを実装するこれらのシェルの中にのみ存在します。

「builtin」と「keyword」の違いの一部は、この歴史によるものです。また、 John1024の回答 で指摘されているように、[[式を解析するための構文規則が異なるという事実を反映しています。⁵


脚注:

  1. そのように見ると、他のほとんどのプログラミング言語でのかっこや角かっこが機能する方法とは異なり、シェルスクリプトで[の周りにスペースを配置する必要がある理由が明らかになります。シェルのコマンドパーサーがif["$x"...を許可した場合、iftest"$x"...も許可する必要があります。

  2. それは1980年頃に起こりました。/bin/[は、1979年の Ancient Unix V7 の私のコピーには存在しません。また、man testがエイリアスとしてドキュメント化していません。 1980年の System III マニュアルのプレリリースコピーにある対応するマニュアルページエントリに、がリストされています

  3. ls -i /bin/[ /bin/test

  4. ただし、この動作を当てにしないでください。 [のBash組み込みバージョンでは]を閉じる必要があり、その組み込みtest実装は、doを指定すると文句を言います。

  5. 組み込みコマンドと外部コマンドの違いは、別の理由で問題になる場合もあります。2つの実装の動作が異なる場合があります。これは、多くのシステムでのechoの場合です。実装は1つしかないため、キーワードを区別する必要はありません。

63
Warren Young

_[_は、当初は単なる外部コマンドであり、_/bin/test_の別名です。ただし、_[_やechoなどのいくつかのコマンドはシェルスクリプトで頻繁に使用されるため、シェルの実装者は、コードをシェル自体に直接コピーすることに決めました。それらが使用される時間。これにより、これらのコマンドは「組み込み」に変わりましたが、フルパスを介して外部プログラムを呼び出すことができます。

_[[_はずっと後で来ました。ビルトインはシェルの内部で実装されていますが、外部コマンドと同様に解析されます。 John1024の回答で説明されているように、これは引用符で囲まれていない変数がWord分割を実行し、_>_や_<_などのトークンがI/Oリダイレクトとして処理されることを意味します。これにより、複雑な比較式を書くのが不便になりました。 _[[_は、シェル構文として作成されたので、理想的に構文解析できます。 _[[_内では、変数はWord分割を取得しません。_<_および_>_は比較演算子として使用できます。_=_は、次のパラメーターが引用符で囲まれているかどうかによって動作が異なります。など。これらはすべて、従来の_[[_コマンド/ビルドよりも_[_を使いやすくする便利なものです。

何百万ものスクリプトに対する互換性のない変更であったため、このような構文として_[_を単純に再コーディングすることはできませんでした。以前は存在しなかった新しい_[[_構文を使用することで、上位互換性のある方法での使用方法を全面的に改善できます。

これは、従来のexprコマンドにほとんど取って代わった算術式の$((...))構文をもたらした進化と似ています。

3
Barmar

bashの新しい[[[の-​​最適化です。

古典的な[には、些細な操作を頻繁に行う場合に大きな欠点が1つあります。それは、毎回新しいプロセスを生成します。
01を比較するためだけに新しいアドレススペースが作成されます!毎回!)

[[の追加の主なポイントは、[内の式の評価が余分なプロセスを生成しないことでした。しかし、[の動作は変更できませんでした。混乱を招き、問題を引き起こします。そのため、最適化は新しい名前で、より効率的な方法で実装されました。つまり、シェル組み込みコマンドです。
これは、副作用としてシェル構文のキーワードになりました。

[が最初に使用されたとき、外部プロセスでそれを行うにはそれは正しい方法でしたです。

0
Volker Siegel