2

bash では、複数のコマンドをチェーンして、すべて stdin から同じ入力を取得する方法はありますか? つまり、1 つのコマンドが stdin を読み取り、何らかの処理を行い、出力をファイルに書き込みます。チェーンの次のコマンドは、最初のコマンドが取得したものと同じ入力を取得します。等々。

たとえば、コンテンツをフィルタリングすることによって、大きなテキスト ファイルを複数のファイルに分割するとします。このようなもの:

cat food_expenses.txt | grep "coffee" > coffee.txt | grep "tea" > tea.txt | grep "honey cake" > cake.txt

2 番目の grep は元のテキスト ファイルではなく、最初の grep の出力を取得するため、これは明らかに機能しません。ティーを挿入しようとしましたが、役に立ちません。最初のgrepが出力ではなく入力をパイプに送信するようにするbashマジックはありますか?

ところで、ファイルの分割は簡単な例でした。ネットワーク経由で受信する連続ライブ テキスト ストリームを分割 (パターン検索によるファイリング) し、出力を別の名前付きパイプまたはソケットに書き込むことを検討してください。シェルスクリプトで簡単にできる方法があれば教えていただきたいです。

(この質問は、不明確さを指摘した回答に基づいて、私の以前の質問のクリーンアップされたバージョンです)

4

8 に答える 8

10

この例では、半ば役に立たない提案としてawkを使用する必要があります。

ただし、一般に、N個の任意のプログラムに単一の入力ストリームのコピーを読み取らせるにはtee、bashのプロセス出力置換演算子を使用できます。

tee <food_expenses.txt \
  >(grep "coffee" >coffee.txt) \
  >(grep "tea" >tea.txt) \
  >(grep "honey cake" >cake.txt)

>(command)これはbash拡張機能であることに注意してください。

于 2009-09-24T15:39:50.950 に答える
5

明らかな疑問は、なぜこれを 1 つのコマンドで実行したいのかということです。

スクリプトを書きたくないが、並行して実行したい場合、bash はsubshel​​lsの概念をサポートしており、これらは並行して実行できます。コマンドを括弧で囲むことにより、grep (または何でも) を同時に実行できます。

$ (grep coffee food_expenses.txt > coffee.txt) && (grep tea food_expenses.txt > tea.txt) 

上記では、入力ファイルの引数を取るため、cat冗長になる可能性があることに注意してください。grep

(代わりに)さまざまなストリームを介して出力をリダイレクトして遊ぶことができます。stdout/stderr に限定されませんが、必要に応じて新しいストリームを割り当てることができます。ここで例を紹介する以外に、これについてこれ以上アドバイスすることはできません

于 2009-06-12T10:36:26.197 に答える
2

の代わりに使用するStephen のアイデアが気に入っています。 awkgrep

きれいではありませんが、出力リダイレクトを使用してすべてのデータの流れを維持するコマンドを次に示しますstdout

cat food.txt | 
awk '/coffee/ {print $0 > "/dev/stderr"} {print $0}' 
    2> coffee.txt | 
awk '/tea/ {print $0 > "/dev/stderr"} {print $0}' 
    2> tea.txt

ご覧のとおり、awk「coffee」に一致するすべての行を に送信しstderr、コンテンツに関係なくすべての行を に送信するために使用しますstdout。次にstderr、ファイルにフィードされ、このプロセスが「お茶」で繰り返されます。

各ステップでコンテンツを除外したい場合は、これを使用できます。

cat food.txt | 
awk '/coffee/ {print $0 > "/dev/stderr"} $0 !~ /coffee/ {print $0}' 
    2> coffee.txt | 
awk '/tea/ {print $0 > "/dev/stderr"} $0 !~ /tea/ {print $0}' 
    2> tea.txt
于 2009-06-12T12:30:14.487 に答える
1

フィルタリングをさまざまなステップで実行する必要がある理由がわかりません。1つのawkプログラムですべての着信行をスキャンし、適切な行を個々のファイルにディスパッチできます。これは、複数のセカンダリコマンドをフィードできる非常に単純なディスパッチです(つまり、出力ファイルの新しい入力を監視する永続的なプロセス、またはファイルは事前にセットアップされ、awkプロセスによって書き込まれるソケットである可能性があります)。

すべてのフィルターにすべての行を表示させる理由がある場合は、「次へ」を削除するだけです。ステートメント、およびすべてのフィルターはすべての行を参照します。

$ cat split.awk
BEGIN{}
/^coffee/ {
    print $0 >> "/tmp/coffee.txt" ;
    next;
}
/^tea/ {
    print $0 >> "/tmp/tea.txt" ;
    next;
}
{ # default
    print $0 >> "/tmp/other.txt" ;
}
END {}
$
于 2009-06-24T01:00:32.890 に答える
1

を使用awkして、最大 2 つのファイルに分割できます。

awk '/Coffee/ { print "Coffee" } /Tea/ { print "Tea" > "/dev/stderr" }' inputfile > coffee.file.txt 2> tea.file.txt
于 2009-06-12T11:07:27.383 に答える
0

入力が無限ではないと仮定すると(ネットワークストリームの場合、閉じる予定がない場合など)、サブシェルを使用してデータを一時ファイルに入れ、次に他の一連のサブシェルを使用してデータを読み取ることを検討します。私はこれをテストしていませんが、おそらく次のようになります{cat inputstream> tempfile}; {grep tea tempfile> tea.txt}; {grepコーヒーtempfile>coffee.txt};

ただし、入力ストリームのサイズが制限されていない場合、ファイルが大きくなりすぎるという洗練された解決策はわかりません。

于 2009-06-12T13:43:08.150 に答える
0

おそらく、簡単な AWK スクリプトを記述して、これを 1 回で行うことができます。あなたのファイルのフォーマットをもう少し説明してもらえますか?

  • スペース/カンマで区切られていますか?
  • 列がスペース、コンマなどの区切り文字で定義されている特定の「列」にアイテムの説明がありますか?

複数のgrepを実行する余裕がある場合、これは機能しますが、

grep coffee food_expanses.txt> coffee.txt
grep tea food_expanses.txt> tea.txt

等々。

于 2009-06-12T10:35:50.690 に答える