web-dev-qa-db-ja.com

プロセスの置換とパイプ

私は次のことを理解する方法を考えていました:

コマンドの標準出力を別の標準入力にパイプすることは、強力なテクニックです。しかし、複数のコマンドのstdoutをパイプ処理する必要がある場合はどうでしょうか。ここでプロセス置換が行われます。

言い換えれば、プロセス置換はパイプができることは何でもできるのですか?

プロセス置換は何ができますが、パイプはできませんか?

89
Tim

それらの違いを理解する良い方法は、コマンドラインで少し実験することです。 <文字の使用における視覚的な類似性にもかかわらず、リダイレクトまたはパイプとは非常に異なる何かを行います。

テストにはdateコマンドを使用してみましょう。

$ date | cat
Thu Jul 21 12:39:18 EEST 2011

これは無意味な例ですが、catがSTDINのdateの出力を受け入れ、それを吐き出したことを示しています。同じ結果は、プロセスの置き換えによっても達成できます。

$ cat <(date)
Thu Jul 21 12:40:53 EEST 2011

しかし、舞台裏で起こったことだけが異なっていました。 STDINストリームが与えられる代わりに、catには実際に開いて読み取るために必要なファイルの名前が渡されました。このステップは、echoの代わりにcatを使用して確認できます。

$ echo <(date)
/proc/self/fd/11

Catはファイル名を受け取ると、ファイルの内容を読み取ってくれます。一方、echoは渡されたファイルの名前を示しただけです。置換をさらに追加すると、この違いはより明確になります。

$ cat <(date) <(date) <(date)
Thu Jul 21 12:44:45 EEST 2011
Thu Jul 21 12:44:45 EEST 2011
Thu Jul 21 12:44:45 EEST 2011

$ echo <(date) <(date) <(date)
/proc/self/fd/11 /proc/self/fd/12 /proc/self/fd/13

プロセス置換(ファイルを生成する)と入力リダイレクト(ファイルをSTDINに接続する)を組み合わせることができます。

$ cat < <(date)
Thu Jul 21 12:46:22 EEST 2011

見た目はほとんど同じですが、今回はcatにファイル名ではなくSTDINストリームが渡されました。エコーで試してみてください:

$ echo < <(date)
<blank>

EchoはSTDINを読み取らず、引数が渡されなかったため、何も取得されません。

パイプと入力リダイレクトは、コンテンツをSTDINストリームに押し込みます。プロセス置換はコマンドを実行し、その出力を特別な一時ファイルに保存してから、コマンドの代わりにそのファイル名を渡します。どのコマンドを使用しても、それはファイル名として扱われます。作成されたファイルは通常のファイルではなく、不要になったときに自動的に削除される名前付きパイプであることに注意してください。

140
Caleb

ここでは、他の方法では不可能なプロセス置換で実行できる3つのことを示します。

複数のプロセス入力

diff <(cd /foo/bar/; ls) <(cd /foo/baz; ls)

パイプでこれを行う方法はありません。

STDINの維持

次のものがあるとします。

curl -o - http://example.com/script.sh
   #/bin/bash
   read LINE
   echo "You said ${LINE}!"

そして、それを直接実行したいとします。以下は無残に失敗します。 Bashは既にSTDINを使用してスクリプトを読み込んでいるため、他の入力はできません。

curl -o - http://example.com/script.sh | bash 

しかし、この方法は完全に機能します。

bash <(curl -o - http://example.com/script.sh)

アウトバウンドプロセスの置換

また、プロセス置換は逆の方法でも機能します。だからあなたはこのようなことをすることができます:

(ls /proc/*/exe >/dev/null) 2> >(sed -n \
  '/Permission denied/ s/.*\(\/proc.*\):.*/\1/p' > denied.txt )

これは少し複雑な例ですが、stdout/dev/nullに送信し、stderrをsedスクリプトにパイプして、ファイルの名前を抽出します。 「アクセスが拒否されました」エラーが表示され、THOSEの結果がファイルに送信されます。

最初のコマンドとstdoutリダイレクトが括弧(サブシェル)内にあるため、THATコマンドの結果のみが/dev/nullに送信され、残りの行を混乱させます。

26
tylerl

Posixシェルにはプロセス置換がないため、bashまたはその他の高度なシェルについて話していると思います。

bashマニュアルページレポート:

プロセス置換
プロセス置換は、名前付きパイプ(FIFO)またはオープンファイルに名前を付ける/ dev/fdメソッドをサポートするシステムでサポートされています。 <(リスト)または>(リスト)の形式を取ります。プロセスリストは、入力または出力がFIFOまたは/ dev/fd内のファイルに接続された状態で実行されます。このファイルの名前は、次の結果として現在のコマンドに引数として渡されます。 >(list)フォームが使用されている場合、ファイルへの書き込みはリストの入力を提供します。<(list)フォームが使用されている場合、引数として渡されたファイルを読み取り、listの出力を取得する必要があります。

使用可能な場合、プロセスの置換は、パラメーターと変数の拡張、コマンドの置換、および算術拡張と同時に実行されます。

つまり、実用的な観点からは、次のような表現を使用できます。

<(commands)

パラメータとしてファイルを必要とする他のコマンドのファイル名として。または、そのようなファイルにリダイレクトを使用できます。

while read line; do something; done < <(commands)

あなたの質問に戻ると、プロセスの置換とパイプはあまり共通点がないように思えます。

複数のコマンドの出力を順番にパイプ処理する場合は、次のいずれかの形式を使用できます。

(command1; command2) | command3
{ command1; command2; } | command3

しかし、プロセス置換でリダイレクトを使用することもできます

command3 < <(command1; command2)

最後に、command3はファイルパラメータを受け入れます(標準入力の代わりに)

command3 <(command1; command2)
26
enzotib

コマンドがファイルのリストを引数として取り、それらのファイルを入力(または出力ですが、一般的ではありません)として処理する場合、これらの各ファイルは名前付きパイプまたはプロセスの置換によって透過的に提供される/ dev/fd疑似ファイルにすることができます。

$ sort -m <(command1) <(command2) <(command3)

Sortはコマンドラインで入力ファイルのリストを取得できるため、これは3つのコマンドの出力を「パイプ」してソートします。

10
camh

プロセス置換は、commandの出力をファイルとして使用する<(command)の形式に限定されないことに注意してください。 commandへの入力としてファイルを供給する>(command)の形式にすることもできます。これは、@ enzotibの回答のbashマニュアルの引用でも言及されています。

上記の_date | cat_の例の場合、>(command)形式のプロセス置換を使用して同じ効果を達成するコマンドは、

_date > >(cat)
_

>(cat)の前に_>_が必要であることに注意してください。これは、@ Calebの回答のように、echoによって明確に説明できます。

_$ echo >(cat)
/dev/fd/63
_

したがって、余分な_>_がなければ、date >(cat)は_date /dev/fd/63_と同じになり、stderrにメッセージを出力します。

ファイル名のみをパラメーターとして受け取り、stdinまたはstdoutを処理しないプログラムがあるとします。これを説明するために、単純化されたスクリプト_psub.sh_を使用します。 _psub.sh_の内容は

_#!/bin/bash
[ -e "$1" -a -e "$2" ] && awk '{print $1}' "$1" > "$2"
_

基本的には、両方の引数がファイル(必ずしも通常のファイルではない)であることをテストし、これが当てはまる場合は、awkを使用して_"$1"_から_"$2"_の各行の最初のフィールドを書き込みます。次に、これまでに述べたことをすべて組み合わせたコマンドは、

_./psub.sh <(printf "a a\nc c\nb b") >(sort)
_

これは印刷されます

_a
b
c
_

と同等です

_printf "a a\nc c\nb b" | awk '{print $1}' | sort
_

しかし、以下は機能せず、ここではプロセス置換を使用する必要があります。

_printf "a a\nc c\nb b" | ./psub.sh | sort
_

または同等の形式

_printf "a a\nc c\nb b" | ./psub.sh /dev/stdin /dev/stdout | sort
_

上記のほかに_./psub.sh_がstdinも読み取る場合、そのような同等の形式は存在せず、その場合、プロセス置換の代わりに使用できるものはありません(もちろん、名前付きパイプまたは一時ファイルを使用しますが、それは別の話です)。

3
Weijun Zhou