Bash内蔵制御構造/コマンドリダイレクトの仕組み

Bash内蔵制御構造/コマンドリダイレクトの仕組み

私が興味を持っているのは、bashの組み込み制御構造と機能を使用して技術的にリダイレクトを実装する方法です。

たとえば、次のコマンドがあります。

while read line; do echo $line; done < lines.txt | tac > ~/reversed.txt

標準入力(lines.txt)をreadコマンド(引数)whileに接続するメカニズムとは何doですか?本体を標準出力(パイプ)に接続するメカニズムは何ですか?明らかに、いくつかのコンテキストルールが適用されますが(外部コマンドリダイレクトとは対照的に)、これらのルールは正確に何であり、bashはそれらを技術的にどのように実装しますか?

答え1

一般に、UNIXシェルは、オープンで必要なopenファイルをパフォーマンス上の理由で(非常に高価であるため)組み込みコマンドと異なる場合がありますが、意味は同じです。forkdup2execveforkexecve

ただし、コマンドライン解析ルールを参照している場合は、これがPOSIXで説明されています。組み込みプログラムと外部プログラムを区別しません。

答え2

ファイル記述子のリダイレクトd0ファイルの内外への転送には、次の手順が含まれます。

  1. ファイルを開きます。ファイルがファイル記述子で開きます。d1
  2. コピー記述子d0現在使用されていないファイル記述子にd2それより大きいd0。これはF_DUPFD次のコマンドで実行できます。fcntlシステムコール。もしd0開かないと、このステップでは何もしません。
  3. コピーd1到着d0。これはF_DUPFDまたはで行うことができます。dup2
  4. 閉鎖d1

繰り返しシャッフルが必要なのは、ファイルを開くときにアプリケーションがファイル記述子を選択できないためです。次の条件が満たされたら、手順2~4を省略できます。d1=d0ただし、シェルはこれを保証できません。

外部コマンドにリダイレクトを適用すると、子プロセスが作成され、子プロセスで実行されます。forkただし、外部コマンドを実行する前にexecve。リダイレクトが内部シェルコマンド(関数呼び出し、ループなど)に適用される場合、これらのステップは元のプロセスで実行され、シェルはリダイレクトされたコマンドの完了後に元のファイル記述子の状態をコピーして復元する必要があります。d2に戻るd0そして閉じるd2(またはちょうど閉じてください。d0最初に開いていない場合)。

パイプには同様の手順が含まれていますが、パイプを作成すると2つのファイル記述子(読み取り側と書き込み側)が生成され、2つの子プロセスがあるため、もう少し複雑です。

  1. パイプラインの作成pipe。システムpipeコールはファイル記述子のペアを返します。アル字型勝つ}。

  2. パイプの左側:

    1. 閉鎖アル字型
    2. コピーを作成してランダムに移動勝つ1として。
  3. パイプの右側:

    1. 閉鎖勝つ
    2. コピーを作成してランダムに移動アル字型0に。
  4. 子プロセスで実行されているパイプラインの両側のシェルで親プロセスが閉じられます。アル字型そして勝つ、パイプの両側が終了するまで待ちます。

    親プロセスのパイプの右側で実行されるシェルでは、シェルは左側が終了するのを待ってから0を閉じ、元のファイル記述子を0から復元します。

ソースコードを読み取るか、デバッガを使用して実行を追跡することで、シェルの機能について学ぶことができます。たとえば、Linuxでは実行中のシステムコールを表示します。

strace sh -c '…'

1古代シェル(POSIXより前)は、子プロセスからリダイレクトされた複雑なコマンドを実行したため、リダイレクトの回復は不要です。

関連情報