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

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

私の知る限りでは[[そうです。強化されたバージョン[なのに、[[キーワードで見て内蔵に出てくると[混乱しますね。

[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]

[これはmakeと[[キーワードにする必要はありませんか?ここで何か抜けましたか?返品、このリンクもう一度確認すると、[同じ[[カテゴリに属しているはずです。

答え1

[との違いは[[非常に基本的です。

  • [命令だ。その引数は他のコマンド引数と同じように扱われます。たとえば、次のことを考えてみましょう。

    [ -z $name ]
    

    シェルが拡張されて$name実行されます。両方結果は他のコマンドと同様にトークン化され、ファイル名が生成されます。

    たとえば、次の操作は失敗します。

    $ 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
    

一般化する

[シェルで実行される他のすべてのコマンドと同じ規則に従うコマンド。

しかし、[[それはコマンドではなくキーワードなので、シェルで特に扱われ、非常に異なる規則に従って動作します。

答え2

存在するV7 Unix- Bourneシェルが最初に登場した場所 - は銀で[始まりtestました/bin/test。したがって、今日作成するコードは次のようになります。

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

あなたは書くでしょう

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

2番目の表記法は依然として存在し、これが何が起こっているのかをより明確にします。test引数を評価し、次を返すコマンドを呼び出しています。終了ステータスコードif次に何をするかを決定するために使用されます。コマンドはシェルに組み込まれていても外部プログラムでもかまいません。

[後で交換としてtest。 ²の組み込み同義語である可能性がありますが、組み込まれていないシェルの最新システムでも利用できますtest/bin/[

[そして、test同じコードを使用して達成できます。これはOS X/bin/[の場合です/bin/testハードリンク同じ実行可能ファイルに。したがって、実装は末尾を完全に無視します]。 asで呼び出す必要はなく/bin/[、asで呼び出す必要はありません。する提供されます/bin/test.⁴

[[という名前の元のプログラムがなかったので、この記録のどれも違いはありません[[。拡張として実装するシェルにのみ存在します。POSIXシェル

「組み込み」と「キーワード」の違いのいくつかは、これらの歴史によるものです。これはまた、[[以下で指摘したように、式を解析するための構文規則が異なることを反映しています。John1024の答え.⁵


脚注:

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

  2. 1980年ごろに起こったことだ。/bin/[私のコピーには存在しません古代UNIX V7また、1979年以降はman testエイリアスとして記録されませんでした。該当するマニュアルページのエントリに試用版があります。システム31980年マニュアル、はいリスト。

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

  4. しかし、このような行動は期待しないでください。 Bashの組み込みバージョンはオフにする必要が[あります]。そうすると、組み込みのtest実装は苦情を示します。する提供してください。

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

答え3

[もともとは外部コマンドでしたが、/bin/test一部のコマンド(および[echoはシェルスクリプトであまりにも頻繁に使用されていたため、シェル実装者は使用するたびに他のコマンドを実行せずにコードをシェル自体に直接コピーすることにしました。プロセス。これにより、これらのコマンドは「組み込みコマンド」に変わりますが、フルパスで外部プログラムを引き続き呼び出すことができます。

[[とても遅くなってきました。組み込みコマンドはシェルの内部で実装されますが、外部コマンドと同じ方法で解析されます。 John1024の回答で説明したように、これは引用符で囲まれていない変数がトークンをトークン化し、そのよう>なトークンが<I / Oリダイレクトとして処理されることを意味します。これにより、複雑な比較式を作成するのが不便になります。[[特別に解析できるようにシェル構文で作成されます。変数は内部的に[[トークン化されず、比較演算子として使用でき、<次の引数が引用されるかどうかによって異なる動作をします。これは、従来のコマンド/組み込みコマンドよりも使いやすい利便性です。>=[[[

[何百万ものスクリプトに対して互換性のない変更になる可能性がある構文では、単に再コーディングすることはできません。以前に存在しなかった新しい構文を使用することで、親[[互換性のある方法で使用方法を完全に変更できます。

$((...))これは、本質的に伝統的な命令に代わる算術式構文の進化と似ていますexpr

答え4

最新の銀[[bash最適化[

古典的な[方法は、マイナーなタスクを実行するために頻繁に使用されるという点で大きな欠点があります。毎回新しいプロセスを作成します。 (毎回合計を
比較するために新しいアドレス空間を作成します!)01

追加する重要な点の1つ[[は、内部表現の評価が[追加パスを生成しないことです。しかし、[仕事が進む方法は変わりません。これは多くの混乱と問題を引き起こします。したがって、最適化は、新しいより効率的な名前のシェル組み込みとして実装されます。
副作用としてシェル構文のキーワードになります。

当時[最初に使用されたのは、これが正しい方法です。これを行うには、外部プロセスを使用します。

関連情報