web-dev-qa-db-ja.com

パイプとリダイレクトをダッシュ​​で理解する

誰かが2つのコマンドの出力をファイルとして別のコマンドに渡す方法を尋ねたところ、以下の answer が得られました。

( cmd1 | ( cmd2 | ( main_command /dev/fd/3 /dev/fd/4 ) 4<&0 ) 3<&0 )

これを開梱する必要があります。

テキストファイルsome_fileがあり、それをmain_commandへの入力として渡したいとします。 main_commandは2つのファイルを入力として受け取ります。 main_commandsome_fileとともに使用し、コマンドcmd2の出力を使用する場合、その1つの方法は次のとおりです。

( cmd2 | ( main_command some_file /dev/fd/4 ) 4<&0 )
  • これの「最も深い」部分(つまり、すべてが最高潮に達する場所)はmain_command some_file /dev/fd/4です。これは、ファイルsome_file/dev/fd/4を引数としてmain_commandに渡すだけです。
  • 4<&0の部分は、stdinがファイル記述子4を指すことを示しています。
  • cmd2 |は、cmd2の出力を次の入力に接続します。
  • かっこの機能が何なのかよくわかりません。それらは単に構文解析の目的で存在するのでしょうか、それともそれ以上のものでしょうか?

私の質問は次のとおりです。

  1. 質問の最初にコマンドを解凍するにはどうすればよいですか?
  2. 括弧は何をしますか?
  3. より単純なコマンドの私の説明は正しいですか?

編集:私の論理が正しければ、1と答える必要はないと言ったはずです。

2
Redirect

これはかなり複雑なコマンドです。私は最後にあなたの質問に直接答えましたが、それまでのすべてはコマンド自体を解凍することです。私は包括的にしようとしたので、場所によっては必要以上に詳細があるかもしれません。

括弧はサブシェルを作成します

_( x y z )
_

現在のシェルから新しいシェルをフォークし、で_x y z_を実行する(そして現在のシェルに戻る)ことを意味します。サブシェルは現在のサブシェルに関するすべてを継承しますが、別のプロセスです。つまり、サブシェルに入力をパイプで送信し、親に影響を与えない独自の環境変更を内部に含めることができます。

開いているすべてのファイルには、数値の「ファイル記述子」が関連付けられています。このコンテキストでの「ファイル」には、実際のファイル、ソケット、標準I/Oストリームなど、あらゆる種類の入力ストリームまたは出力ストリームが含まれます。番号は、 C read function で直接使用できるハンドルであり、話しているストリームを識別し、オペレーティングシステムによって提供される対応するシステムコールと他のすべてのIO関数。

_4<&0_ リダイレクトを実行します 標準入力ファイル記述子(0)をファイル記述子4として複製します。つまり、FD 0は4にコピーされ、その逆ではありません。この場合、リダイレクトに先行するサブシェルの開いているファイルを変更しています。今のところ、それは入力ストリームの別の「名前」を作成しているだけです。ただし、重要な部分は、2つの名前がその後互いに独立していることです。FD0が別のものを参照するように変更され、2つが分岐した場合でも、FD4は常に同じストリームを参照します。

_/dev/fd/4_は、プログラムが独自の開いているファイル記述子にアクセスするための(非標準の)方法です。 Linuxでは、これは_/proc/self/fd_へのシンボリックリンクであり、現在のプロセスのファイル記述子テーブルを具体化します。プログラムはopen("/dev/fd/4", O_RDONLY)を実行して、このプログラムがFD 4に持つストリーム(_4_自体など)を参照するファイルハンドルを取得できます。プログラムに関する限り、これは他のファイルと同じように開いたり、閉じたり、読み取ったりできる通常のファイルです。開いているファイル記述子はサブプロセスによって継承されるため、_main_command_はその内部のサブシェルと同じファイル記述子4を持ち、_/dev/fd/4_もそこで機能します。

_cmd2 | x_は_cmd2_を実行し、その標準出力をxの標準入力(またはFD 0)に接続します。コマンドでは、xはサブシェル式です。


私たちの全体的なコマンド

_cmd2 | ( main_command /dev/fd/4 ) 4<&0
_

次に、3つの主要な部分があります。

  1. _cmd2_を実行し、その出力を_( main_command /dev/fd/4 ) 4<&0_にパイプします。
  2. _4_を_0_の_( main_command /dev/fd/4 )_(標準入力)で識別されるストリームの別の名前にします。
  3. 引数として_main_command_を指定して_/dev/fd/4_を実行します。これは、(おそらく)ファイルとして開かれ、そこから読み取られ、_cmd2_の出力を取得します。

最終的な効果は、_main_command_が開くことができるパス名引数を取得し、_cmd2_の出力を読み取ることができることです。これは、Bashプロセス置換main_command <(cmd2)の場合とまったく同じです。実際、引数として_/dev/fd/63_を指定する可能性があります。それ以外の場合は、内部で非常によく似ています。


完全なコマンドについて

_( cmd1 | ( cmd2 | ( main_command /dev/fd/3 /dev/fd/4 ) 4<&0 ) 3<&0 )
_

サブシェルをネストしました。これは、標準入力の2つのコピーを作成するためですが、2つの異なる標準入力です。1つは_cmd1_の出力です。大きい方のサブシェルにパイプされた後にFD3に送られ、もう1つは_cmd2_の出力であり、最も内側のサブシェルにパイプされた後にFD4に入れられます。 2つの_0_は両方とも標準入力を参照しますが、各コマンドの標準入力は、異なるものがパイプされているため、区別されます。

それが問題の最も紛らわしい部分だと思います。各コマンド(ここでは、各サブシェル)には、独自の標準入力があり、_cmd1_または_cmd2_からパイプされ、その一意の標準入力ストリームは次のようにエイリアスされます。 _3_または_4_。これらの開いているファイル記述子は、サブシェルコマンドと子コマンドの次のレイヤーに継承されるため、標準入力が別のものを指している場合でも、最も内側のコマンドの_/dev/fd/3_は外部で行ったのと同じことを指します。

外側の括弧は厳密には必須ではありませんが、一部のコマンドでは少し堅牢になり、おそらく良い習慣になります。内部のものは次のとおりです。これらは、独自のリダイレクトセットを内部に持つことができる新しいサブプロセスを作成するために使用され、独自の標準入力ストリームがパイプされます。

最も内側のリダイレクトは実際には冗長です。標準入力にそれ以上の変更が加えられていないため、_cmd2 | main_command /dev/fd/3 /dev/stdin_も機能します。


質問に直接対処するには:

  1. 質問の最初にコマンドを解凍するにはどうすればよいですか?

    開梱は、この時点までの投稿全体です。

  2. 括弧は何をしますか?

    括弧はサブシェルを作成します。これは、入力をパイプするなど、他のコマンドと同じように使用できる独立したシェルプロセスですが、リダイレクトなどの通常のシェル操作を内部で実行できます。

  3. より単純なコマンドの私の説明は正しいですか?

    部分的に。 _4<&0_は、ファイル記述子4がstdinを指し、重要なことに現在stdinと呼ばれているものを指していることを示しています-標準入力の概念を指していません。 _/dev/fd/4_は、「すべてがファイルである」という意味の「ファイル」ですが、より具体的には、開いたときにFD4を返すパス名です。

5
Michael Homer