So verarbeiten Sie jede Zeile, die als Ergebnis des Befehls grep empfangen wurde

152

Ich habe eine Reihe von Zeilen aus einer Datei abgerufen, nachdem der Befehl grep wie folgt ausgeführt wurde:

var=`grep xyz abc.txt`

Nehmen wir an, ich habe 10 Zeilen, die als Ergebnis aus xyz bestehen.

Jetzt muss ich jede Zeile verarbeiten, die ich als Ergebnis des Befehls grep erhalten habe. Wie gehe ich vor?

XYZ_Linux
quelle
6
Keine der Antworten hier erwähnt die Macht grep -ofür solche Dinge. Das -oFlag gibt nur den übereinstimmenden Text mit einer Übereinstimmung pro Ausgabezeile zurück. (Es ist nicht erschöpfend, echo aaa |grep 'a*'gibt Ihnen also nur "aaa" und lässt die drei Teilübereinstimmungen "", "a" und "aa" weg))
Adam Katz

Antworten:

269

Eine der einfachen Möglichkeiten besteht darin, die Ausgabe nicht in einer Variablen zu speichern, sondern sie direkt mit einer while / read-Schleife zu durchlaufen.

Etwas wie:

grep xyz abc.txt | while read -r line ; do
    echo "Processing $line"
    # your code goes here
done

Es gibt Variationen dieses Schemas, je nachdem, wonach Sie suchen.

Wenn Sie Variablen innerhalb der Schleife ändern müssen (und diese Änderung außerhalb der Schleife sichtbar sein soll), können Sie die Prozessersetzung verwenden, wie in der Antwort von fedorqui angegeben :

while read -r line ; do
    echo "Processing $line"
    # your code goes here
done < <(grep xyz abc.txt)
Matte
quelle
Wenn es keine Linie mit xyz gibt?
XYZ_Linux
Dann passiert nichts, die Schleife wird nicht ausgeführt.
Mat
13
Das Problem bei dieser Methode ist, dass sich (aufgrund der Pipe) alles innerhalb der Schleife in einer Unterschale befindet. Wenn Sie also Variablen festlegen, die außerhalb der Schleife während der Schleife definiert sind, werden ihre Werte nach der Schleife nicht verfügbar!
David Doria
3
@ David: bot eine Alternative, um Ihr Anliegen anzusprechen. (Fedorqui hatte es auch schon angesprochen.)
Mat
Für Befehle, deren letzte Ausgabezeile nicht mit einer neuen Zeile abgeschlossen wird, benötigen Sie: while read p || [[ -n $p ]]; do ...(ausgeliehen von stackoverflow.com/questions/1521462/… )
Ohad Schneider
21

Sie können die folgende while readSchleife ausführen, die vom Ergebnis des grepBefehls mit der sogenannten Prozesssubstitution gespeist wird :

while IFS= read -r result
do
    #whatever with value $result
done < <(grep "xyz" abc.txt)

Auf diese Weise müssen Sie das Ergebnis nicht in einer Variablen speichern, sondern die Ausgabe direkt in die Schleife "einspeisen".


Beachten Sie die Verwendung von IFS=und read -rgemäß den Empfehlungen in BashFAQ / 001: Wie kann ich eine Datei (Datenstrom, Variable) Zeile für Zeile (und / oder Feld für Feld) lesen? ::

Die Option -r zum Lesen verhindert die Interpretation von Backslashs (normalerweise als Backslash-Zeilenumbruchpaar verwendet, um über mehrere Zeilen fortzufahren oder um die Trennzeichen zu umgehen). Ohne diese Option werden alle nicht entkoppelten Backslashes in der Eingabe verworfen. Sie sollten fast immer die Option -r beim Lesen verwenden.

Im obigen Szenario verhindert IFS = das Trimmen von führenden und nachfolgenden Leerzeichen. Entfernen Sie es, wenn Sie diesen Effekt wünschen.

Die Prozessersetzung wird auf der Seite mit den Bash-Hackern erläutert :

Die Prozessersetzung ist eine Form der Umleitung, bei der die Eingabe oder Ausgabe eines Prozesses (eine Folge von Befehlen) als temporäre Datei angezeigt wird.

fedorqui 'SO hör auf zu schaden'
quelle
OK, ich habe die forVersion angestrichen . Es wurde versucht, eine Schleife "${$(grep xyz abc.txt)[@]}"wie in stackoverflow.com/a/14588210/1983854 durchzuführen , konnte dies jedoch nicht. Also lasse ich einfach die erste Version.
Fedorqui 'SO hör auf zu schaden'
1
Sie können die Parametererweiterung nicht auf eine Befehlssubstitution anwenden (es sei denn, Sie verwenden zsh, wo diese Art der Verschachtelung wahrscheinlich funktioniert).
Chepper
Ein mögliches Problem mit dieser Redewendung ist, dass, wenn etwas in der Schleife versucht, von der Standardeingabe zu lesen, es Teil der Datei wird. Um diese Möglichkeit zu vermeiden, sende ich die Datei lieber über den Dateideskriptor 3 als über stdin. Verwenden Sie einfach while IFS= read -r result <&3unddone 3< <(grep ...
Gordon Davisson
8

Ich würde vorschlagen, hier awk anstelle von grep + etwas anderes zu verwenden.

awk '$0~/xyz/{ //your code goes here}' abc.txt

Julien Grenier
quelle
1
Wie würden Sie die gesamte gefundene Zeile in referenzieren //your code goes here?
user857990
2
@ user857990 Die gesamte Zeile wird in AWK als $ 0 dargestellt.
Julien Grenier
2

Ohne Iteration mit der Option --line-buffered grep:

your_command | grep --line-buffered "your search"

Beispiel aus dem wirklichen Leben mit einem Symfony PHP Framework-Router-Debug-Befehl ouput, um alle "API" -bezogenen Routen zu erfassen:

php bin/console d:r | grep --line-buffered "api"
Nicolas Bonnici
quelle
Die einzige Lösung, die funktioniert, wenn Ihr_Befehl lange läuft (wie tail -f some.login meinem Fall) ...
Izkata
0

Oft spielt die Reihenfolge der Verarbeitung keine Rolle. GNU Parallel ist für diese Situation gemacht:

grep xyz abc.txt | parallel echo do stuff to {}

Wenn Ihre Verarbeitung eher wie folgt ist:

grep xyz abc.txt | myprogram_reading_from_stdin

und myprogramist langsam dann kannst du laufen:

grep xyz abc.txt | parallel --pipe myprogram_reading_from_stdin
Ole Tange
quelle
0

Durchlaufen Sie die grep-Ergebnisse mit einer while / read-Schleife. Mögen:

grep pattern filename.txt | while read -r line ; do
    echo "Matched Line:  $line"
    # your code goes here
done
Abhi km
quelle
0

Für diejenigen, die einen Einzeiler suchen:

grep xyz abc.txt | while read -r line; do echo "Processing $line"; done
Laurent
quelle