2 Stimmen

Kann ich mehrere Befehle aneinanderreihen und dafür sorgen, dass sie alle dieselbe Eingabe von stdin erhalten?

Gibt es in der Bash eine Möglichkeit, mehrere Befehle zu verketten, die alle die gleiche Eingabe von stdin erhalten? Das heißt, ein Befehl liest stdin, führt eine Verarbeitung durch und schreibt die Ausgabe in eine Datei. Der nächste Befehl in der Kette erhält dieselbe Eingabe, die der erste Befehl erhalten hat. Und so weiter.

Nehmen wir zum Beispiel eine große Textdatei, die durch Filterung des Inhalts in mehrere Dateien aufgeteilt werden soll. Etwa so:

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

Dies funktioniert offensichtlich nicht, denn der zweite grep erhält die Ergebnisse des ersten grep Ausgabe und nicht die ursprüngliche Textdatei. Ich habe versucht, einzufügen T-Stücke aber das hilft nicht. Gibt es irgendeine Bash-Magie, die den ersten grep dazu bringt, seine Eingabe in die Leitung, nicht in den Ausgang?

Übrigens war die Aufteilung einer Datei ein einfaches Beispiel. Stellen Sie sich vor, Sie würden einen kontinuierlichen Live-Textstrom, der über ein Netzwerk kommt, aufteilen (durch Mustersuche filtern) und die Ausgabe in verschiedene benannte Pipes oder Sockets schreiben. Ich würde gerne wissen, ob es eine einfache Möglichkeit gibt, dies mit einem Shell-Skript zu tun.

(Diese Frage ist eine bereinigte Version meiner früher auf der Grundlage von Antworten, die auf die Unklarheit hinwiesen)

10voto

Mark Edgar Punkte 4467

Für dieses Beispiel sollten Sie, wie von semiuseless vorgeschlagen, awk verwenden.

Aber um N beliebige Programme eine Kopie eines einzelnen Eingabestroms lesen zu lassen, kann man im Allgemeinen verwenden tee und der Bash-Operator zur Ersetzung der Prozessausgabe:

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

Beachten Sie, dass >(command) ist eine Bash-Erweiterung.

5voto

Brian Agnew Punkte 260470

Die offensichtliche Frage ist, warum Sie dies mit einem einzigen Befehl tun wollen?

Wenn Sie kein Skript schreiben wollen und Dinge parallel laufen lassen wollen, unterstützt die Bash die Konzepte von Unterschalen und diese können parallel laufen. Indem Sie Ihren Befehl in Klammern setzen, können Sie Ihre Greps (oder was auch immer) gleichzeitig laufen lassen, z.B.

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

Beachten Sie, dass in der obigen Tabelle Ihr cat überflüssig sein kann, da grep benötigt ein Argument aus einer Eingabedatei.

Sie können (stattdessen) mit der Umleitung der Ausgabe durch verschiedene Streams herumspielen. Sie sind nicht auf stdout/stderr beschränkt, sondern können nach Bedarf neue Streams zuweisen. Ich kann Ihnen dazu nicht mehr raten, als Sie auf Beispiele zu verweisen aquí

2voto

Nate Kohl Punkte 34194

Ich mag Stephen's Idee der Verwendung awk anstelle von grep .

Es ist nicht schön, aber hier ist ein Befehl, der die Ausgabeumleitung verwendet, damit alle Daten durchfließen 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

Wie Sie sehen können, verwendet es awk um alle Zeilen, die auf 'Kaffee' passen, an stderr und alle Zeilen unabhängig vom Inhalt an stdout . Dann stderr wird in eine Datei eingespeist, und der Vorgang wird mit "tea" wiederholt.

Wenn Sie den Inhalt bei jedem Schritt herausfiltern möchten, könnten Sie dies verwenden:

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

1voto

Stephen Darlington Punkte 50435

Sie könnten verwenden awk in bis zu zwei Dateien aufzuteilen:

awk '/Coffee/ { print "Coffee" } /Tea/ { print "Tea" > "/dev/stderr" }' inputfile > coffee.file.txt 2> tea.file.txt

1voto

Dennis Williamson Punkte 322329

Hier sind zwei bash Skripte ohne awk . Die zweite verwendet nicht einmal grep !

Mit grep:

#!/bin/bash
tail -F food_expenses.txt | \
while read line
do
    for word in "coffee" "tea" "honey cake"
    do
        if [[ $line != ${line#*$word*} ]]
        then
            echo "$line"|grep "$word" >> ${word#* }.txt # use the last word in $word for the filename (i.e. cake.txt for "honey cake")
        fi
    done
done

Ohne grep:

#!/bin/bash
tail -F food_expenses.txt | \
while read line
do
    for word in "coffee" "tea" "honey cake"
    do
        if [[ $line != ${line#*$word*} ]] # does the line contain the word?
        then
            echo "$line" >> ${word#* }.txt # use the last word in $word for the filename (i.e. cake.txt for "honey cake")
        fi
    done
done;

編集する。

Hier ist eine AWK-Methode:

awk 'BEGIN {
         list = "coffee tea"; 
         split(list, patterns)
     }
     {
         for (pattern in patterns) {
             if ($0 ~ patterns[pattern]) {
                 print > patterns[pattern] ".txt"
             }
         }
     }' food_expenses.txt

Die Arbeit mit Mustern, die Leerzeichen enthalten, muss noch geklärt werden.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X