プロセス置換とパイプ


86

私は次を理解する方法を疑問に思っていました:

コマンドのstdoutを別のコマンドのstdinにパイプすることは強力な手法です。しかし、複数のコマンドの標準出力をパイプする必要がある場合はどうでしょうか?これがプロセス置換の出番です。

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

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

回答:


134

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

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

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

これは無意味な例ですが、STDIN catの出力を受け入れ、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

ほぼ同じように見えますが、今回はファイル名ではなくSTDINストリームがcatに渡されました。これは、echoで試してみるとわかります。

$ echo < <(date)
<blank>

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

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


正しく理解した場合、tldp.org / LDP / abs / html / process-sub.html#FTN.AEN18244は、プロセス置換により名前付きパイプではなく一時ファイルが作成されると言います。私の知る限り、namedは一時ファイルを作成しません。パイプへの書き込みには、ディスクへの書き込みは含まれません:stackoverflow.com/a/6977599/788700
Adobe

私はこの答えが合法であることを知っています ' grok :D
aqn

2
@Adobeでは、一時ファイルプロセスの置換が生成する名前付きパイプであるかどうかを確認できます[[ -p <(date) ]] && echo true。これはtrue、bash 4.4または3.2で実行すると生成されます。
デノボ

24

bashposixシェルにはプロセス置換がないので、あなたが話しているのか、他の高度なシェルであると思います。

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

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

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

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

<(commands)

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

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

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

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

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

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

command3 < <(command1; command2)

最後に、command3ファイルパラメータを受け入れる場合(stdinの代わりに)

command3 <(command1; command2)

<()と<<()は同じ効果をもたらしますよね?
ソルフィッシュ

@solfish:正確ではありません:ファイル名が必要な場所であればどこでもfirseを使用できます。2番目はそのファイル名の入力リダイレクトです
-enzotib

23

ここでは、他の方法では不可能なプロセス置換を使用して実行できる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/nullstderrをsedスクリプトにパイプして「Permission denied」エラーが表示されたファイルの名前を抽出し、その結果をファイルに送信します。

最初のコマンドとstdoutリダイレクションは括弧(subshel​​l)で囲まれているため、THATコマンドの結果のみが送信さ/dev/nullれ、残りの行を混乱させないことに注意してください。


このdiff例では、cd失敗する可能性がある場合に注意する必要があることに注意してくださいdiff <(cd /foo/bar/ && ls) <(cd /foo/baz && ls)
phk

「stderrのパイピング中」:これはパイピングではなく、fifoファイルを通過するという点ではありませんか?
ゴーティエ

@Gauthierいいえ; コマンドはfifoではなく、ファイル記述子への参照で置き換えられます。だから、「エコー<(エコー)」読み取りまたはFD番号63から書き込み特殊文字デバイスである「は/ dev / FD / 63」のようなもの、得られるはず
tylerl

10

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

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

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


1
IIRC <(command)構文はbashのみの機能です。
フィロマス

@Philomath:ZSHにもあります。
カレブ

まあ、ZSHにはすべてがあります...(または少なくともしようとします)。
フィロマス

@Philomath:プロセス置換は他のシェルでどのように実装されていますか?
-camh

4
@Philomathは<()、多くの高度なシェル機能と同様に、もともとksh機能であり、bashとzshで採用されました。psub具体的には魚の機能であり、POSIXとは関係ありません。
ジル

3

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

date | cat上記の例では、フォームのプロセス置換を使用し>(command)て同じ効果を達成するコマンドは、

date > >(cat)

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

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

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

パラメータとしてファイル名のみを使用し、stdinまたはを処理しないプログラムがあるとしますstdoutpsub.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、そのような同等のフォームは存在せず、その場合、プロセス置換の代わりに使用できるものはありません(もちろん、名前付きパイプまたは一時ファイルを使用することもできますが、それは別のものです物語)。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.