$ PATHでの検索は内部ではどのように機能しますか?


8

そこにいる道の人を教えてウェブ上であまりにも多くの記事/リソースHOW環境変数を設定するためにPATH、彼らは短いの手使用できるように、javaまたはpython代わりに、コマンドラインインタフェースに絶対パスのなど。

私が知りたいのは、コマンドを入力してEnter キーを押すと、背後にあるものです(ブラウザーでURLを入力すると何が起こるかと同様です)。

これが私の推測です:

  1. コマンドを読み取ります(正しい引数を取得するために標準入力を解析/前処理します$@
  2. コマンド検索
  3. コマンドの実行(プログラムの開始、メモリの消費、シェルへのstdout / stderr)
  4. 関連する環境変数(例えばによってエミュレータを再レンダリングする$PS#$PROMPTなど)

私が最も理解したいのはコマンド検索です。明らかに、$PATHいくつかのバックグラウンド関数によって消費され、区切り文字として:/ ;で区切られた後、何が起こりましたか?ハッシュテーブル(キー:ファイルのベース名、値:ファイルの絶対ディレクトリ名)を使用して、これらのPATHまたはその他のフックの下にバイナリファイルを保存しますか?

注:[ -z hash [command] ]コマンドが現在の環境で使用できるかどうかを確認するために使用できるので、当初はハッシュテーブルであると思っていましたが、使用するhash | grep pythonwhich python、期待どおりに動作している間、出力から何も得られません。(メカニズムはシェル固有の可能性があると思いますが、それについてもっと洞察を得たいです。)

回答:


11

ご想像のとおり、正確な動作はシェルに依存していますが、機能のベースラインレベルはPOSIXによって指定されています。

標準のシェルコマンド言語(ほとんどのシェルはそのスーパーセットを実装しています)のコマンド検索と実行には多くのケースがありますが、ここでPATHは、が使用されている場合のみに関心があります。その場合:

XBD環境変数で説明されているように、PATH環境変数を使用してコマンドが検索されます。

そして

検索が成功した場合:

[...]

シェルは、別のユーティリティ環境でユーティリティを実行execl()し、パス引数を検索の結果得られたパス名に設定して関数[...] を呼び出すのと同等のアクションを実行します。

失敗した場合、実行は失敗し、終了コード127がエラーメッセージとともに返されます。

この動作はexecvp、特に関数と一致しています。すべてのexec*関数は、実行するプログラムのファイル名、一連の引数(argvプログラムのもの)、および環境変数のセットを受け入れます。PATHルックアップを使用するバージョンの場合、POSIXは次のように定義します。

引数ファイルは、新しいプロセスイメージファイルを識別するパス名を作成するために使用されます[...]このファイルのパスプレフィックスは、環境変数PATHとして渡されたディレクトリの検索によって取得されます


PATH動作は、他の場所で次のように定義されています。

この変数は、特定の関数とユーティリティがファイル名だけでわかる実行可能ファイルを検索する際に適用する一連のパスプレフィックスを表すものとします。接頭辞は、<コロン>( ':')で区切られます。ゼロ以外の長さのプレフィックスがこのファイル名に適用されるとき、プレフィックスがで終わっていない場合、プレフィックスとファイル名の間に<slash>が挿入されます。長さゼロのプレフィックスは、現在の作業ディレクトリを示すレガシー機能です。これは、2つの隣接する文字( "::")、リストの残りの前にある最初の<コロン>、またはリストの残りに続く末尾の<コロン>として表示されます。厳密に準拠するアプリケーションは、実際のパス名(。など)を使用して、PATHの現在の作業ディレクトリを表します。リストは最初から最後まで検索され、指定された名前と適切な実行権限を持つ実行可能ファイルが見つかるまで、各プレフィックスにファイル名が適用されます。探しているパス名に<スラッシュ>が含まれている場合、パス接頭辞による検索は実行されません。パス名が<スラッシュ>で始まる場合、指定されたパスは解決されます(パス名の解決を参照)。PATHが設定されていないかnullに設定されている場合、パス検索は実装定義です。

それは少し密度が高いので、要約:

  1. プログラム名に/(スラッシュ、U + 002F SOLIDUS)が含まれている場合は、通常の方法でパスとして扱い、このプロセスの残りをスキップします。シェルの場合、このケースは技術的には発生しません(シェルルールですでに対処されているため)。
  2. の値はPATH各コロンで断片に分割され、各コンポーネントは左から右に処理されます。特別な(歴史的な)ケースとして、空でない変数の空のコンポーネントは.(現在のディレクトリ)として扱われます。
  3. 各コンポーネントについて、プログラム名が最後に追加され、結合が行わ/れ、その名前のファイルの存在が確認されます。存在する場合は、有効な実行(+ x)​​権限も確認されます。これらのチェックのいずれかが失敗した場合、プロセスは次のコンポーネントに進みます。それ以外の場合、コマンドはこのパスに解決され、検索が実行されます。
  4. コンポーネントが不足すると、検索は失敗します。
  5. に何もないPATH、または存在しない場合は、好きなようにしてください。

実際のシェルには、このルックアップの前に見つかる組み込みコマンドがあり、多くの場合、エイリアスと関数も含まれます。それらはと相互作用しませんPATHPOSIXはそれらの周りのいくつかの動作を定義し、あなたのシェルにはもっと多くの動作があるかもしれません。


これのexec*大部分を実行することに依存することは可能ですが、実際にはシェルは、特にキャッシュの目的でこのルックアップ自体を実装する場合がありますが、空のキャッシュの動作は同様です。シェルはここではかなり広い緯度を持ち、コーナーケースでは微妙に異なる動作をします。

ご覧のとおり、Bash はハッシュテーブル使用して、以前に表示されたコマンドの完全なパスを記憶していhashます。このテーブルには関数を使用してアクセスできます。コマンドを初めて実行すると検索され、結果が見つかるとテーブルに追加されるので、次に試すときにわざわざ探す必要はありません。

一方、zshでは、PATH通常、シェルの起動時にフルが検索されます。ルックアップテーブルには、検出されたすべてのコマンド名があらかじめ入力されているため、通常、ランタイムルックアップは必要ありません(新しいコマンドが追加されない限り)。これまでに存在しなかったコマンドをタブ補完しようとすると、このことに気付くでしょう。

などの非常に軽量なシェルは、dashできるだけ多くの動作をシステムライブラリに委任する傾向があり、過去のコマンドパスを覚えておく必要はありません。


このような詳細な説明に感謝します。これは本当に深い洞察を与えます。あなたの比較PATHの間bashzsh、私は私の混乱を解決するのに役立ちます!
Xlee
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.