Linuxの匿名および名前付きパイプ
1. 概要
Linuxコマンドラインインターフェイスを使用する場合、プログラムの出力を別のプログラムへの入力として使用するようにリダイレクトするのが一般的です。
このチュートリアルでは、Linuxでのパイプと名前付きパイプの使用について説明します。
2. パイプとは何ですか?
パイプはUnixベースのシステムの重要なメカニズムであり、ディスクに何も保存せずに、あるプロセスから別のプロセスにデータを通信できるようにします。
Linuxには、パイプ(無名パイプまたは名前なしパイプとも呼ばれます)とFIFO(名前付きパイプとも呼ばれます)の2種類のパイプがあります。
3. パイプ
パイプは、パイプ文字’|‘で区切られたコマンドをつなぎ合わせることによって使用されます。 これはパイプラインと呼ばれることが多く、各シェルはその動作を定義します。
シェルは、左端のコマンドから始めて、バックグラウンドで実行されている個別のプロセスで各コマンドを実行します。
次に、左側のコマンドの標準出力が右側のコマンドの標準入力に接続されます。
このメカニズムは、パイプライン内のすべてのプロセスが完了するまで続きます。
BashやZshなどのシェルはトークン「 |& 」はパイプラインを指し、左側のコマンドの標準出力と標準エラーの両方を右側のコマンドの標準入力に接続します。
netstatコマンドを使用して、ローカルホストを使用して実行されているプロセスを確認し、grepユーティリティでフィルタリングするとします。
$ netstat -tlpn | grep 127.0.0.1
(Not all processes could be identified, non-owned process info
will not be shown, you would have to be root to see it all.)
tcp 0 0 127.0.0.1:3306 0.0.0.0:* LISTEN -
この出力例では、フィルターに関係なく、netstatにスクリプトのstdoutとstderrの両方が表示されます。
それでは、stderrをstdoutにマージして、grepのstdinに渡します。
$ netstat -tlpn |& grep 127.0.0.1
tcp 0 0 127.0.0.1:3306 0.0.0.0:* LISTEN -
そして、警告メッセージを抑制しました。
3.1. Bashのパイプライン
Bashには、 PIPESTATUS という変数があります。この変数には、最後に実行されたパイプラインのプロセスからの終了ステータスのリストが含まれています。
$ exit 1 | exit 2 | exit 3 | exit 4 | exit 5
$ echo ${PIPESTATUS[@]}
1 2 3 4 5
パイプライン全体の実行の戻りステータスは、pipefail変数のステータスによって異なります。
この変数が設定されている場合、パイプラインの戻りステータスは、ゼロ以外のステータスを持つ右端のコマンドの終了ステータスになるか、すべてのコマンドが正常に終了した場合はゼロになります。
$ set -o pipefail
$ exit 1 | exit 2 | exit 3| exit 4 | exit 0
$ echo $?
4
pipefail オプションを無効にすると、パイプの戻りステータスは最後のコマンドの終了ステータスになります。
$ set +o pipefail
$ exit 1 | exit 2 | exit 3| exit 4 | exit 0
$ echo $?
0
Bashには、現在の環境のフォアグラウンドで最後のコマンドを実行するようにシェルに指示するlastpipeオプションもあります。
3.2. Zshのパイプライン
Zshは、パイプラインをBashと同様に制御しますが、いくつかの違いがあります。 たとえば、Zshには pipestatus コマンドがあります。これは、BashのPIPESTATUS変数に似ています。
さらに、Zshは、現在のシェル環境で実行される最後のコマンドを除いて、各パイプラインのコマンドを別々のプロセスで実行します。
4. 名前付きパイプ
名前付きパイプとも呼ばれるFIFOは、パイプに似た特殊なファイルですが、ファイルシステムに名前が付いています。 複数のプロセスがこの特別なファイルにアクセスして、通常のファイルと同じように読み取りと書き込みを行うことができます。
したがって、名前は、ファイルシステムで名前を使用する必要があるプロセスの参照ポイントとしてのみ機能します。
FIFOには、他のファイルと同じ特性があります。 たとえば、所有権、権限、メタデータがあります。
FIFOのもう1つの重要な機能は、双方向通信を提供することです。
Linuxでは、コマンド mknod (FIFOタイプを示すために文字「p」を使用)および mkfifo :を使用してFIFOを作成できます。
$ mkfifo pipe1
$ mknod pipe2 p
$ ls -l
prw-r--r-- 1 cuau cuau 0 Oct 7 21:17 pipe1
prw-r--r-- 1 cuau cuau 0 Oct 7 21:17 pipe2
ここでは、FIFOのファイルタイプが文字「p」で示されていることがわかります。
このメカニズムにより、シェルを使用してより複雑なアプリケーションを作成できます。
名前付きパイプと匿名パイプを一緒に使用できます。 FIFOとパイプの両方を組み合わせたリバースシェルを作成しましょう。
ncユーティリティを使用して、「サーバー」側がシェルを提供し、「クライアント」側がそれにアクセスできるクライアント/サーバーアプリケーションを作成します。
まず、インストールしましょう netcat-openbsd パッケージ
$ sudo apt install netcat-openbsd
次に、fifo_reverseという名前のFIFOを作成してmkfifofifo_reverse。と入力します。
次に、それぞれが「クライアント」(たとえば「user1」)および「サーバー」(たとえば「user2」)として機能する2人の異なるユーザーでログインしましょう。 user2シェルでこのパイプラインを実行してみましょう。
user2_prompt$ bash -i < fifo_reverse |& nc -l 127.0.0.1 1234 > fifo_reverse
このワンライナーでは、シェルがFIFOのコンテンツを読み取り、それをインタラクティブなBashシェルに渡します。
次に、インタラクティブシェルのstdoutとstderrの両方が nc コマンドに渡されます。このコマンドは、アドレス127.0.0.1のポート1234でリッスンします。
そして最後に、「クライアント」が接続を正常に確立すると、 nc は受信したものをFIFOに書き込み、インタラクティブシェルは受信したものを実行できるようになります。
ここで、user1シェルを使用して、次のように入力します。
user1_prompt$ nc 127.0.0.1 1234
user2_prompt$
そして、user2プロンプトを取得しましたが、匿名パイプと名前付きパイプを組み合わせたuser1シェルを使用しています。
5. 一時的な名前付きパイプ
一部のシェルには、プロセス置換と呼ばれる機能があり、コマンドのリストの入力または出力をFIFOに接続します。 コマンドはこのFIFOの名前を使用します。
BashとZshでのこのメカニズムの表記は次のとおりです。 <(コマンドリスト) リストの結果を実際のコマンドの標準入力に渡す、または >(コマンドリスト) 実際のコマンドの標準出力をリストの標準入力に渡します。
これまで見てきたことを使用して、複数のコマンドの出力をwcコマンドに渡します。
$ wc -l \
<(find / -mindepth 1 -maxdepth 1 -type d) \
<(find /opt -mindepth 1 -maxdepth 1 -type d)
20 /proc/self/fd/11
2 /proc/self/fd/12
22 total
この出力例では、 find コマンドを使用して、/および/optディレクトリ内のディレクトリの数を取得します。
6. 名前付きパイプまたは匿名パイプをいつ使用するのですか?
名前付きパイプの代わりに匿名パイプを使用することは、探している特性によって異なります。 それらのいくつかは、永続性、双方向通信、ファイル名の取得、フィルターの作成、アクセス許可の制限などです。
たとえば、コマンドの出力を複数回フィルタリングする場合は、無名パイプを使用するのが最も適切なオプションのようです。 また、匿名パイプを使用する場合は、使用しているシェルが中心的な役割を果たすことを忘れないでください。
一方、ファイル名が必要で、データをディスクに保存したくない場合は、FIFOを探しています。 参照として名前だけが必要な場合は、別のプロセスから直接取得したコンテンツを使用します。
また、無名パイプは配管タイプのパイプのように見えますが、FIFOはより複雑な図を作成できることを考慮してください。
7. 結論
パイプとFIFOは、Linuxコマンドラインで作業するときに非常に便利です。 このチュートリアルでは、両方の機能のいくつかを確認し、一方を他方の代わりに使用する場合について説明しました。