私は次のことを理解する方法を考えていました:
コマンドの標準出力を別の標準入力にパイプすることは、強力なテクニックです。しかし、複数のコマンドのstdoutをパイプ処理する必要がある場合はどうでしょうか。ここでプロセス置換が行われます。
言い換えれば、プロセス置換はパイプができることは何でもできるのですか?
プロセス置換は何ができますが、パイプはできませんか?
それらの違いを理解する良い方法は、コマンドラインで少し実験することです。 <
文字の使用における視覚的な類似性にもかかわらず、リダイレクトまたはパイプとは非常に異なる何かを行います。
テストには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ストリームに押し込みます。プロセス置換はコマンドを実行し、その出力を特別な一時ファイルに保存してから、コマンドの代わりにそのファイル名を渡します。どのコマンドを使用しても、それはファイル名として扱われます。作成されたファイルは通常のファイルではなく、不要になったときに自動的に削除される名前付きパイプであることに注意してください。
ここでは、他の方法では不可能なプロセス置換で実行できる3つのことを示します。
diff <(cd /foo/bar/; ls) <(cd /foo/baz; ls)
パイプでこれを行う方法はありません。
次のものがあるとします。
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
に送信され、残りの行を混乱させます。
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)
コマンドがファイルのリストを引数として取り、それらのファイルを入力(または出力ですが、一般的ではありません)として処理する場合、これらの各ファイルは名前付きパイプまたはプロセスの置換によって透過的に提供される/ dev/fd疑似ファイルにすることができます。
$ sort -m <(command1) <(command2) <(command3)
Sortはコマンドラインで入力ファイルのリストを取得できるため、これは3つのコマンドの出力を「パイプ」してソートします。
プロセス置換は、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
も読み取る場合、そのような同等の形式は存在せず、その場合、プロセス置換の代わりに使用できるものはありません(もちろん、名前付きパイプまたは一時ファイルを使用しますが、それは別の話です)。