必要な作業を頻繁に行いたい。
stdout
パイプライン(パイプと呼ばれる)をpipeline-before
2つの並列ストリームに分割します。- 結果ストリーム(
stdin
)を2つの別々のパイプ(pipeline-between-0
およびpipeline-between-1
)に供給します。 - 2つの結果
stdout
ストリームのマージ厳格な順序で; - マージされた結果ストリームを
stdin
別のパイプ(pipeline-after
)に供給します。
(3)で「厳密な順序で」とは、すべての出力が出力の前にマージpipeline-between-0
された出力ストリームに表示されることを意味しますpipeline-between-1
。
すべてをグラフィカルに表現できます。
pipeline-before --.--- pipeline-between-0 -.
\ \
`- pipeline-between-1 ---`-- pipeline-after
pipeline-between-0
これら/ペアの例はpipeline-between-1
次のとおりです。
head -n 1 | tr 'a-z' 'A-Z'
tail -n +2 | sort -t $'\t' -k1,1
英語では、この組み合わせを「最初の行をすべて大文字にし、残りの行を最初の列に並べ替える」と説明します。
尋ねる:そのようなタスクを表現するための一般的なシェル構文はありますか?
私はこの質問に対する答えに興味がありますzsh
。bash
一般的に言えば、これは構文です。いいえ働く:
$ pipeline-before | tee >( pipeline-between-0 ) | pipeline-between-1 | pipeline-after
この構文は、次の2つの理由で失敗します。
- の一部の出力がの
pipeline-between-1
一部の出力の前に表示されることがよくありますpipeline-between-0
。 SIGPIPE
結局、出力が切り捨てられます(信号によるものと思われます)。
私は次を試しました運動(私は100%理解していないことを認めます):
{
pipeline-before |
{ tee >( pipeline-between-0 4>&1 1>&3 ); } |
pipeline-between-1
} 3>&1 | pipeline-after
AFAICT、この構文は上記の最初の問題を解決するようです(つまり、いくつかの非公式テストに基づくpipeline-between-0
出力はpipeline-between-1
正しい順序で表示されます)。しかし、残念ながら、少なくともいくつかのケースでは、最終出力はまだ切り捨てられます。
答え1
たぶん名前付きパイプ(FIFO)のみが必要な場合があります。
以下の例:
{ seq 1 100000 | grep 1$ & seq 1 100000 | grep 2$ ; } > unsorted
ファイル内の1と2で終わる数字の混合を返す必要がありますunsorted
。私たちはそれらをソートしたいので(すべての数字は1で終わり、すべての数字は2で終わる)、各結果に1つずつ2つの名前付きパイプを作成し、必要な順序で組み合わせます。
mkfifo stream{1,2}
{ seq 1 100000 | grep 1$ >stream1 & seq 1 100000 | grep 2$ > stream2 ; } &\
cat stream1 stream2 > sorted
ファイルの順序が同じであることを確認してください。
diff -q {un,}sorted
(それらは異なる必要があります)ソートされた順序が期待どおりであることを確認してください。
sed 1,10000q sorted | grep 2$
(結果があってはならず、unsorted
ファイルはデータを返す必要があります)
bash
名前付きパイプは同じように機能する必要がありますzsh
。
または、擬似コードの最も一般的な表現は次のとおりです。
tee
入力ストリーム(「パイプ前」)は、FIFOを介して複数の並列ストリームにコピーされますin-i
。- 各ストリームは異なる命令によって並列に処理され、対応する出力はストリーム
out-i
(「i間のパイプ」)に送られる。 - 必要な順序で出力ストリームを接続し、次のコマンド(「アフターパイプ」)に渡します。
mkfifo {in,out}-{0..n}
pre-cmd | tee in-0 in-1 ... in-n | cat >/dev/null &
cmd-0 <in-0 >out-0 &
cmd-1 <in-0 >out-0 &
....
cmd-i <in-n >out-n &
cat out-0 out-1 ... out-n | after-cmd
私は一般化の理由でcat >/dev/null
代わりに使用しています。同じ内容は(おそらく)最終的に重複します。cat >in-n
cat
例:
mkfifo {in,out}-{0..2}
seq 0 100 | tee in-{0..2} | cat >/dev/null &
grep '33$' <in-0 >out-0 &
awk '$1<2' <in-1 >out-1 &
sed '/^.\{1,2\}$/d' <in-2 >out-2 &
cat out-2 out-0 out-1 | tr '\n' '-'
結果:100-33-0-1-
答え2
あなたはそれを探していますかparallel --tee
?出力に十分な空きディスク容量がある限り、任意のサイズ/tmp
の入力を簡単に処理できます。
(printf "Header1\tHeader2\n"; paste <(seq 20 -1 11) <(seq 10) ) |
parallel -k --pipe --tee ::: "head -n 1 | tr 'a-z' 'A-Z'" "tail -n +2 | sort -t $'\t' -k1,1"
またはbash機能と同じです。
pipeline-before() {
printf "Header1\tHeader2\n"
paste <(seq 20 -1 11) <(seq 10)
}
pipeline-between-0() {
head -n 1 | tr 'a-z' 'A-Z'
}
pipeline-between-1() {
tail -n +2 | sort -t $'\t' -k1,1
}
pipeline-after() {
echo "This is pipeline-after"
cat
echo "Done"
}
export -f pipeline-before pipeline-between-0 pipeline-between-1 pipeline-after
pipeline-before |
parallel -k --pipe --tee ::: pipeline-between-0 pipeline-between-1 |
pipeline-after
そうでない場合は、より多くの入力、出力、およびパイプライン - *が何であるかについての例を詳しく説明できますか?