次のことを理解する方法を知りたいです。
あるコマンドの標準出力を別のコマンドの標準入力にパイプすることは強力な技術です。しかし、複数のコマンドの標準出力をパイプする必要がある場合はどうなりますか?ここでプロセス置換が開始されます。
つまり、パイプができることをプロセス交換ができますか?
パイプはできませんが、プロセスの置き換えは何ができますか?
答え1
これらの違いを理解する良い方法は、コマンドラインでいくつかの実験を実行することです。<
役割の使用は視覚的に似ていますが、リダイレクトやパイプとは全く異なる動作をします。
date
テストにコマンドを使用します。
$ date | cat
Thu Jul 21 12:39:18 EEST 2011
これは無意味な例ですが、cat
STDINから出力を取得しdate
て吐き出すことを示しています。手続き型置換を使用しても同じ結果が得られます。
$ cat <(date)
Thu Jul 21 12:40:53 EEST 2011
しかし、後ろで何が起こるかは異なります。cat
実際に渡されるのは、STDINストリームを提供するのではなく、開いて読み取るために必要なファイル名です。代わりに、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を使用してこれを確認できます。
$ echo < <(date)
<blank>
echoはSTDINを読み取らず、パラメータも渡されないため、何も得られません。
パイプと入力リダイレクトは、コンテンツをSTDINストリームにプッシュします。プロセス置換はコマンドを実行し、その出力を特別な一時ファイルに保存し、コマンドの代わりにそのファイル名を渡します。どのコマンドを使用しても、それをファイル名として扱います。生成されたファイルは通常のファイルではなく名前付きパイプであり、不要になると自動的に削除されます。
答え2
以下は、プロセス置換によって達成できる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 )
これは少し複雑な例ですが、次のように送信されます。標準出力に/dev/null
同時にパイプライン標準エラーsedスクリプトで「許可拒否」エラーを示すファイル名を抽出し、その結果をファイルに送信します。
最初のコマンドと標準出力リダイレクトは角括弧(サブシェル)そのコマンドの結果のみが送信され、/dev/null
残りの行を妨げないようにします。
答え3
bash
私はposixシェルがないので、あなたや他の高度なシェルについて話していると仮定する必要があります。プロセスの交換。
bash
マニュアルページは以下を報告します:
プロセスの交換
プロセス置換は、名前付きパイプ(FIFO)または/ dev / fd名前付きファイルを開く方法をサポートするシステムでサポートされています。 <(リスト)または>(リスト)形式を使用します。プロセスリストの入力または出力は、FIFOまたは/dev/fdのファイルにリンクされます。ファイル名は、拡張結果として現在のコマンドに引数として渡されます。 >(リスト)形式を使用している場合は、ファイルに書き込むとリストへの入力が提供されます。 <(リスト)形式を使用している場合は、リストの出力を取得するには引数として渡されたファイルを読み取る必要があります。
可能であれば、プロセス置換は、パラメータおよび変数拡張、コマンド置換、および算術拡張と同時に実行されます。
つまり、実用的な観点から見ると、次のような表現を使うことができます。
<(commands)
ファイルを引数として要求する他のコマンドのファイル名。または、次のようにファイルのリダイレクトを使用できます。
while read line; do something; done < <(commands)
あなたの質問に戻って、プロセスの置き換えとパイプには共通点がないようです。
複数のコマンドの出力を順次パイプするには、次のいずれかの形式を使用できます。
(command1; command2) | command3
{ command1; command2; } | command3
ただし、プロセスの交換時にリダイレクトを使用することもできます。
command3 < <(command1; command2)
最後にcommand3
(stdinの代わりに)ファイル引数を受け入れると
command3 <(command1; command2)
答え4
<(command)
プロセスの交換はフォームに限定されず、出力をcommand
ファイルとして使用することに注意してください。>(command)
ファイルを入力として使用する形式を取ることもできます。command
@enzotibの答えに引用されたbashマニュアルでもこれについて言及されています。
date | cat
上記の例では、>(command)
形式のプロセス置換を使用して同じ効果を得るためのコマンドは次のとおりです。
date > >(cat)
>
前の内容が>(cat)
必須ですのでご注意ください。echo
@Calebの答えはこれを再び明確にします。
$ echo >(cat)
/dev/fd/63
したがって、extrasがない場合は、>
stderrにメッセージを印刷するのと同じですdate >(cat)
。date /dev/fd/63
stdin
ファイル名のみを引数として取り、または処理しないプログラムがあるとしますstdout
。psub.sh
これを説明するために、あまりにも単純化されたスクリプトを使用します。内容psub.sh
は
#!/bin/bash
[ -e "$1" ] && [ -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
場合は、そのエントリは存在せず、この場合はプロセスの置き換えの代わりに何も使用できません(もちろん、名前付きパイプや一時ファイルを使用することもできますが、これは別の話です)。