
In Unix-like computer operating systems, a pipeline is a mechanism for inter-process communication using message passing. A pipeline is a set of processes chained together by their standard streams, so that the output text of each process (stdout) is passed directly as input (stdin) to the next one. The second process is started as the first process is still executing, and they are executed concurrently.
The concept of pipelines was championed by Douglas McIlroy at Unix's ancestral home of Bell Labs, during the development of Unix, shaping its toolbox philosophy. It is named by analogy to a physical pipeline. A key feature of these pipelines is their "hiding of internals". This in turn allows for more clarity and simplicity in the system.
The pipes in the pipeline are anonymous pipes (as opposed to named pipes), where data written by one process is buffered by the operating system until it is read by the next process, and this uni-directional channel disappears when the processes are completed. The standard shell syntax for anonymous pipes is to list multiple commands, separated by vertical bars ("pipes" in common Unix verbiage).
The pipeline concept was invented by Douglas McIlroy[1] and first described in the man pages of Version 3 Unix.[2][3] McIlroy noticed that much of the time command shells passed the output file from one program as input to another. The concept of pipelines was championed by Douglas McIlroy at Unix's ancestral home of Bell Labs, during the development of Unix, shaping its toolbox philosophy.[4][5]
彼のアイデアは1973年に実現され、(マッキルロイは「熱狂的な一夜にして」と書いている)ケン・トンプソンがUnixバージョン3のシェルpipe()といくつかのユーティリティにシステムコールとパイプを追加した。「翌日には」とマッキルロイは続けて、「誰もがパイプの興奮に加わり、忘れられないほどのワンライナーの乱交が見られた」。マッキルロイはまた、バージョン4でパイプ構文の説明を大幅に簡略化した表記法についてもトンプソンに功績を認めている。[ 6 ] [ 2 ]|
Unix パイプは独立して開発されたものの、 1960 年代にダートマス タイムシェアリング システム[ 8 ]のためにケン ロックナー[ 7 ]によって開発された「通信ファイル」と関連があり、またその前身となっている。
Unixのこの機能は、MS-DOSやVM/CMSおよびMVSのCMS Pipelinesパッケージなど、他のオペレーティングシステムにも取り入れられ、最終的にはソフトウェアエンジニアリングにおけるパイプとフィルタの設計パターンとして認識されるようになりました。
トニー・ホーアの通信シーケンシャルプロセス(CSP)では、マキロイのパイプがさらに発展している。[ 9 ]
パイプライン機構は、メッセージパッシングを使用してプロセス間通信を行うために使用されます。パイプラインは、標準ストリームによって連結された一連のプロセスであり、各プロセスの出力テキスト(stdout )が次のプロセスへの入力( stdin )として直接渡されます。2 番目のプロセスは、最初のプロセスがまだ実行されている間に開始され、それらは並行して実行されます。物理的なパイプラインになぞらえて名付けられています。これらのパイプラインの重要な特徴は、「内部構造の隠蔽」です。[ 10 ]これにより、システムの明瞭性とシンプルさが向上します。
ほとんどの Unix ライクなシステムでは、パイプラインのすべてのプロセスが同時に起動され、ストリームが適切に接続され、マシン上で実行されている他のすべてのプロセスとともにスケジューラによって管理されます。Unix パイプを他のパイプ実装と区別する重要な側面は、バッファリング の概念です。たとえば、送信プログラムが毎秒5000バイトを生成し、受信プログラムが毎秒 100 バイトしか受け入れられない場合でも、データは失われません。代わりに、送信プログラムの出力はバッファに保持されます。受信プログラムがデータを読み取る準備ができたら、パイプライン内の次のプログラムがバッファから読み取ります。バッファがいっぱいになると、受信側がバッファから少なくとも一部のデータを取り出すまで、送信プログラムは停止 (ブロック) します。Linux では、バッファのサイズは 16ページであり、ほとんどのシステムで 65,536 バイト (64 KiB) に相当します。[ 11 ]
広く使われているUnixシェルはすべて、パイプラインを作成するための特別な構文構造を備えています。いずれの場合も、コマンドはASCIIの縦棒文字|(このため「パイプ文字」と呼ばれることが多い)で区切って順番に記述します。シェルはプロセスを開始し、それらの標準ストリーム間の必要な接続(バッファ領域の確保を含む)を設定します。
パイプラインは匿名パイプを使用します。匿名パイプの場合、あるプロセスによって書き込まれたデータは、次のプロセスによって読み取られるまでオペレーティングシステムによってバッファリングされ、この一方向チャネルはプロセスが完了すると消滅します。これは、名前付きパイプとは異なります。名前付きパイプでは、ファイルは名前付きパイプとして定義され、メッセージの送受信が行われ、プロセスが完了した後もそのパイプは残ります。匿名パイプの標準的なシェル構文は、縦棒(一般的なUnix用語では「パイプ」)で区切られた複数のコマンドを列挙することです。
コマンド1 |コマンド2 |コマンド3 例えば、現在のディレクトリ内のファイル一覧を表示し(ls)、lsの出力から文字列「key」を含む行のみを抽出し(grep)、その結果をスクロール可能なページで表示する(less)には、ユーザーはターミナルのコマンドラインに次のように入力します。
ls -l | grep key | less このコマンドls -lはプロセスとして実行され、その出力 (stdout) は のプロセスの入力 (stdin) にパイプされgrep key、同様に のプロセスにもパイプされますless。各プロセスは前のプロセスから入力を受け取り、 標準ストリームを介して次のプロセスに出力を生成します。各 は、オペレーティングシステムに実装されている(匿名) パイプと呼ばれるプロセス間通信メカニズム|によって、左側のコマンドの標準出力を右側のコマンドの標準入力に接続するようにシェルに指示します。パイプは一方向であり、データはパイプラインを左から右に流れます。
以下は、 URLで指定されたWebリソースに対してスペルチェックを行うパイプラインの例です。その動作については後述します。
curl 'https://en.wikipedia.org/wiki/Pipeline_(Unix)' |sed 's/[^a-zA-Z ]/ /g' |tr 'AZ ' 'az\n' |grep '[az]' |ソート-u |comm -23 - < ( sort /usr/share/dict/words ) |少ない curlウェブページのHTMLコンテンツを取得します(wget一部のシステムでは使用できます)。sedウェブページの内容に含まれる文字のうち、スペースと文字以外のすべての文字をスペースに置き換えます。(改行は保持されます。)trすべての大文字を小文字に変換し、テキスト行内の空白を改行に変換します(各「単語」が別々の行に表示されます)。grep少なくとも1つの小文字のアルファベットを含む行のみを含めます(空白行は除外します)。sort「単語」のリストをアルファベット順に並べ替え、-uスイッチによって重複を削除します。commは、2 つのファイル間で共通する行を見つけ、-232 番目のファイルに固有の行と両方に共通する行を抑制し、指定された最初のファイルにのみ存在する行のみを残します。-ファイル名の代わりに を使用すると、comm標準入力 (この場合はパイプラインから) が使用されます。は、 の期待どおり、ファイルsort /usr/share/dict/wordsの内容をwordsアルファベット順にソートし、結果を一時ファイル (プロセス置換経由) に出力します。この一時ファイルは を読み取ります。結果は、/usr/share/dict/words に存在しない単語 (行) のリストです。comm<( ... )commlessユーザーが検索結果をページ送りで閲覧できるようにする。デフォルトでは、パイプライン内のプロセスの標準エラー出力ストリーム(" stderr ") はパイプを通して渡されず、代わりにマージされてコンソールに出力されます。ただし、多くのシェルにはこの動作を変更するための追加の構文があります。たとえば、csh|&シェルでは、の代わりにを使用すると、|標準エラー出力ストリームも標準出力とマージして次のプロセスに渡す必要があることを意味します。Bashシェル|&では、バージョン 4.0 以降[ 12 ]または を使用することで、標準エラーを2>&1とマージしたり、別のファイルにリダイレクトしたりすることもできます。
最も一般的に使用されるシンプルなパイプラインでは、シェルはパイプを介して一連のサブプロセスを接続し、各サブプロセス内で外部コマンドを実行します。したがって、シェル自体はパイプラインを流れるデータに対して直接的な処理を行いません。
しかし、シェルは、いわゆるミルまたはパイプミル(while最初のコマンドの結果を「ミル処理」するコマンドが使用されるため)を使用して、直接処理を実行できます。この構造は一般的に次のようになります。
command | while read -r var1 var2 ... ; do # var1、var2などに解析された変数を使用して各行を処理する# (これはサブシェルである可能性があることに注意してください。while ループが終了すると、var1、var2などは使用できなくなります。zsh や新しいバージョンの Korn シェルなど、一部のシェルでは、パイプ演算子の左側のコマンドをサブシェルで処理します)doneこのようなパイプミルは、ループ本体に、から読み込むコマンド(catなど)が含まれている場合、意図したとおりに動作しない可能性があります。[ 13 ]ループの最初の反復で、このようなプログラム(ドレインと呼びましょう)はから残りの出力を読み込み、ループは終了します(結果はドレインの仕様によって異なります)。この動作を回避する方法はいくつかあります。まず、一部のドレインはからの読み込みを無効にするオプションをサポートしています(例:)。あるいは、ドレインが有用な処理を行うためにからの入力を読み込む必要がない場合は、を入力として与えることができます。sshstdincommandstdinssh -nstdin< /dev/null
パイプのすべてのコンポーネントは並列に実行されるため、シェルは通常、その内容を処理するためにサブプロセス(サブシェル)をフォークし、変数の変更を外部のシェル環境に伝播することができません。この問題を解決するために、「pipemill」には、代わりにコマンド置換を含むヒアドキュメントから供給することができ、パイプラインの実行が終了するまで待機してから内容を処理します。あるいは、並列実行には名前付きパイプまたはプロセス置換を使用することもできます。GNU bashには、最後のパイプコンポーネントのフォークを無効にするオプションもあります。 [ 14 ]lastpipe
パイプラインはプログラム制御下で作成できます。Unixpipe()システムコールは、オペレーティングシステムに新しい匿名パイプオブジェクトの構築を要求します。これにより、プロセス内に2つの新しいファイルディスクリプタが作成されます。1つはパイプの読み取り専用側、もう1つは書き込み専用側です。パイプの両端は通常の匿名ファイルディスクリプタのように見えますが、シーク機能はありません。
デッドロックを回避し並列処理を活用するため、1つ以上の新しいパイプを持つUnixプロセスは、通常、fork()新しいプロセスを作成するための関数を呼び出します。各プロセスは、データを生成または消費する前に、使用しないパイプの端を閉じます。あるいは、プロセスは新しいスレッドを作成し、パイプを使用してスレッド間で通信を行うこともできます。
名前付きパイプはmkfifo()、または使用して作成することもできmknod()、プログラムが呼び出されるときに入力ファイルまたは出力ファイルとして提示されます。これにより、マルチパスパイプを作成でき、標準エラーリダイレクトまたはと組み合わせると特に効果的ですtee。
macOS Tahoeが登場するまで、繰り返し実行されるコマンドを連結するためにパイプラインの概念を用いるAppleのAutomatorのアイコンに描かれているロボットは、オリジナルのUnixの概念に敬意を表してパイプを持っていた。
パイプラインはC++で使用できます。C ++20operator|では(パイプ演算子)が導入され、名前空間を使用したLINQスタイルのチェーン操作が可能になりますstd::ranges。にはstd::views、 を介して呼び出されるいくつかのクラスが含まれていますoperator()。[ 15 ]
using std :: vector ; using std :: ranges :: to ; using std :: views :: filter ; using std :: views :: transform ;vector < int > numbers = { 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9 , 10 };// パイプライン: 偶数をフィルタリングし、2 倍にして、結果を合計します。vector < int > result = numbers | filter ([]( int n ) -> bool { return n % 2 == 0 ; }) | transform ([]( int n ) -> int { return n * 2 ; }) | to < vector > ();マキルロイ: それは私が Unix に対して管理上のコントロールをほぼ行使し、それらのことを推進していた数少ない場所の 1 つでした。
{{cite web}}: CS1 maint: 数値名: 著者リスト (リンク)