シェル内の複数のファイルへのパイプ


29

ディスクに保存したくない大量のデータを生成するアプリケーションがあります。アプリケーションは主に、使用したくないデータを出力しますが、個別のファイルに分割する必要がある有用な情報のセットを出力します。たとえば、次の出力がある場合:

JUNK
JUNK
JUNK
JUNK
A 1
JUNK
B 5
C 1
JUNK

次のようにアプリケーションを3回実行できます。

./app | grep A > A.out
./app | grep B > B.out
./app | grep C > C.out

これは私が欲しいものを手に入れるだろうが、時間がかかりすぎるだろう。また、すべての出力を単一のファイルにダンプし、それを解析したくありません。

上記の3つの操作を組み合わせて、アプリケーションを1回実行するだけで3つの個別の出力ファイルを取得する方法はありますか?

回答:


78

ティーがある場合

./app | tee >(grep A > A.out) >(grep B > B.out) >(grep C > C.out) > /dev/null

ここから)

プロセス置換について


4
素晴らしい、これは次のようにレンダリングすることもできます./app | tee >(grep A > A.out) >(grep B > B.out) | grep C > C.out
。– evilsoup

7
質問の元のタイトルが「複数プロセスへのパイプ」であることを考えると、この答えは現在唯一の正確なものです。
-acelent

3
+1。これは、特定のフィルタリングコマンドがであったという事実に依存しないため、最も一般的に適用可能な回答grepです。
-ruakh

1
これは提起された質問に対する最良の回答であり、そのようにマークする必要があることに同意します。 Parallelは別のソリューション(投稿されています)ですが、いくつかの時間比較を行ったので、上記の例の方が効率的です。代わりに、複数のファイル圧縮や複数のmp3変換など、CPUを集中的に使用する操作がopに含まれている場合、並列ソリューションがより効果的であることは間違いありません。
AsymLabs

32

使用できます awk

./app | awk '/A/{ print > "A.out"}; /B/{ print > "B.out"}; /C/{ print > "C.out"}'

6
質問のタイトルは複数のプロセスへのパイプであり、この答えは複数のファイルへの「パイプ」(正規表現によるディスパッチ)に関するものです。この回答は受け入れられたため、質問のタイトルはそれに応じて変更する必要があります。
acelent

@PauloMadeiraあなたは正しい。より良いタイトルになると思いますか?
sj755

私は非常に小さな編集「シェル内の複数のファイルへのパイプ」を提案しました、それは修正待ちです、チェックしてください。コメントが受け入れられたら削除する予定でした。
acelent

@PauloMadeira-タイトルを変更しました。編集内容は表示されませんでしたが、あなたは正しいです。これが受け入れられた回答である場合、タイトルでのプロセスの使用は正しくありませんでした。
slm

17

シェルのパターンマッチング機能を使用することもできます。

./app | while read line; do 
     [[ "$line" =~ A ]] && echo $line >> A.out; 
     [[ "$line" =~ B ]] && echo $line >> B.out; 
     [[ "$line" =~ C ]] && echo $line >> C.out; 
 done

あるいは:

./app | while read line; do for foo in A B C; do 
     [[ "$line" =~ "$foo" ]] && echo $line >> "$foo".out; 
  done; done

次で始まるバックスラッシュと行を処理できるより安全な方法-

./app | while IFS= read -r line; do for foo in A B C; do 
     [[ "$line" =~ "$foo" ]] && printf -- "$line\n" >> "$foo".out; 
  done; done

@StephaneChazelasがコメントで指摘しているように、これはあまり効率的ではありません。おそらく最良の解決策は@AurélienOoms 'です。


これは、入力にバックスラッシュ、空白、ワイルドカード文字、または... で始まる行が含まれていないことを前提としています。また-n-e行ごとに複数のシステムコール(read(2)文字ごとに1 つ、開いているファイル、書き込み行ごとに閉じます...)。一般に、while readシェルでテキストを処理するためにループを使用するのは悪い習慣です。
ステファンシャゼル

@StephaneChazelas回答を編集しました。バックスラッシュ-nなどで動作するようになりました。ただし、両方のバージョンが空白でも問題なく動作することを確認できる限り、間違っているのでしょうか?
テルドン

いいえ、最初の引数printfは形式です。変数を引用符で囲まない理由はありません。
ステファンシャゼル

また、入力にnullがある場合、bash(およびcstringsを同様の方法で使用する他のシェル)で破損します。
クリスダウン

9

複数のコアがあり、プロセスを並列にしたい場合は、以下を実行できます。

parallel -j 3 -- './app | grep A > A.out' './app | grep B > B.out' './app | grep C > C.out'

これにより、並列コアで3つのプロセスが生成されます。コンソールまたはマスターファイルへの出力が必要な場合、出力を混合するのではなく、出力を一定の順序に保つという利点があります。

Ole Tangeのgnuユーティリティparallelは、ほとんどのリポジトリからparallelまたはmoreutilsという名前で入手できます。ソースはSavannah.gnu.orgから入手できます。また、紹介ビデオもこちらにあります

補遺

並列の最新バージョン(必ずしも配布リポジトリのバージョンではない)を使用して、より洗練された構造を使用できます。

./app | parallel -j3 -k --pipe 'grep {1} >> {1}.log' ::: 'A' 'B' 'C'

1つの./appおよび3つの並列grepプロセスを別々のコアまたはスレッドで実行した結果を達成します(並列自体によって決定されるように、-j3もオプションであると考えていますが、この例では参考のために提供されています)。

parallelの新しいバージョンは、次のようにして取得できます。

wget http://ftpmirror.gnu.org/parallel/parallel-20131022.tar.bz2

その後、通常のアンパックを行い、cdでparallel- {date}、。/ configure && make、sudo make installを実行します。これにより、parallel、manページparallel、およびmanページparallel_tutorialがインストールされます。


7

Perlの1つを次に示します。

./app | perl -ne 'BEGIN {open(FDA, ">A.out") and 
                         open(FDB, ">B.out") and 
                         open(FDC, ">C.out") or die("Cannot open files: $!\n")} 
                  print FDA $_ if /A/; print FDB $_ if /B/; print FDC $_ if /C/'

1
sed -ne/A/w\ A.out -e/B/w\ B.out -e/C/p <in >C.out

... <in読み取り可能な場合、3つのoutfileがすべて書き込まれる前に切り捨てられます。

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