Jak przetworzyć każdą linię otrzymaną w wyniku polecenia grep

152

Mam kilka wierszy pobranych z pliku po uruchomieniu polecenia grep w następujący sposób:

var=`grep xyz abc.txt`

Powiedzmy, że mam 10 linii, które w rezultacie składają się z xyz.

Teraz muszę przetworzyć każdą linię, którą otrzymałem w wyniku polecenia grep. Jak mam to zrobić?

XYZ_Linux
źródło
6
Żadna z odpowiedzi tutaj nie wspomina o sile grep -otego rodzaju rzeczy. -oFlaga będzie oddać tylko tekst, który odpowiada z jednego meczu na linię produkcji. (Nie jest wyczerpująca, więc echo aaa |grep 'a*'podaje tylko „aaa” i pomija trzy częściowe dopasowania „”, „a” i „aa”)
Adam Katz,

Odpowiedzi:

269

Jednym z łatwych sposobów nie jest przechowywanie danych wyjściowych w zmiennej, ale bezpośrednie iterowanie po nich za pomocą pętli while / read.

Coś jak:

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

Istnieją różnice w tym schemacie w zależności od tego, czego dokładnie szukasz.

Jeśli chcesz zmienić zmienne wewnątrz pętli (i chcesz, aby ta zmiana była widoczna poza nią), możesz użyć podstawiania procesów zgodnie z odpowiedzią fedorqui :

while read -r line ; do
    echo "Processing $line"
    # your code goes here
done < <(grep xyz abc.txt)
Mata
źródło
jeśli nie ma linii z xyz?
XYZ_Linux
Wtedy nic się nie dzieje, pętla nie jest uruchamiana.
Mat
13
Problem z tą metodą polega na tym, że (ze względu na potok) wszystko wewnątrz pętli znajduje się w podpowłoce, więc ustawienie zmiennych zdefiniowanych poza pętlą podczas pętli nie powoduje udostępnienia ich wartości po pętli!
David Doria
3
@David: stanowi alternatywę dla rozwiązania Twojego problemu. (fedorqui też się tym zajął)
Mat
W przypadku poleceń, których ostatnia linia danych wyjściowych nie jest zakończona znakiem nowej linii, potrzebujesz: while read p || [[ -n $p ]]; do ...(zapożyczone ze stackoverflow.com/questions/1521462/… )
Ohad Schneider
21

Możesz wykonać następującą while readpętlę, która będzie zasilana wynikiem grepkomendy z wykorzystaniem tzw. Podstawienia procesu:

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

W ten sposób nie musisz przechowywać wyniku w zmiennej, ale bezpośrednio „wstrzyknąć” jej wyjście do pętli.


Zwróć uwagę na użycie IFS=i read -rzgodnie z zaleceniami w BashFAQ / 001: Jak odczytać plik (strumień danych, zmienną) wiersz po wierszu (i / lub pole po polu)? :

Opcja -r do odczytu zapobiega interpretacji odwrotnego ukośnika (zwykle używana jako para znaków nowego wiersza z odwrotnym ukośnikiem, aby kontynuować w wielu wierszach lub uniknąć ograniczników). Bez tej opcji wszystkie znaki ukośnika odwrotnego bez zmiany znaczenia w danych wejściowych zostaną odrzucone. Prawie zawsze powinieneś używać opcji -r z read.

W powyższym scenariuszu IFS = zapobiega przycinaniu wiodących i końcowych białych znaków. Usuń go, jeśli chcesz ten efekt.

Jeśli chodzi o zastępowanie procesu, jest to wyjaśnione na stronie hakerów bash :

Podstawianie procesu to forma przekierowania, w której dane wejściowe lub wyjściowe procesu (pewna sekwencja poleceń) pojawiają się jako plik tymczasowy.

fedorqui 'SO przestań szkodzić'
źródło
OK, wybrałem forwersję. Próbowałem wykonać pętlę, "${$(grep xyz abc.txt)[@]}"jak na stackoverflow.com/a/14588210/1983854, ale nie udało się. Zostawiam więc tylko pierwszą wersję.
fedorqui 'SO przestać szkodzić'
1
Nie możesz zastosować interpretacji parametrów do podstawiania poleceń (chyba że używasz zsh, gdzie ten rodzaj zagnieżdżenia prawdopodobnie działa).
chepner
Jednym z możliwych problemów z tym idiomem jest to, że jeśli cokolwiek wewnątrz pętli spróbuje odczytać ze standardowego wejścia, otrzyma część pliku. Aby uniknąć takiej możliwości, wolę wysyłać plik przez deskryptor pliku 3 zamiast standardowego wejścia. Po prostu użyj while IFS= read -r result <&3idone 3< <(grep ...
Gordon Davisson
8

Sugerowałbym użycie awk zamiast grep + coś innego tutaj.

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

Julien Grenier
źródło
1
W jaki sposób odniesiesz się do całej znalezionej linii //your code goes here?
user857990
2
@ user857990 Cała linia jest reprezentowana jako 0 $ w AWK.
Julien Grenier
2

Bez żadnej iteracji z opcją grep --line-buffered:

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

Przykład z życia wzięty z wyprowadzeniem polecenia debugowania routera w Symfony PHP Framework w celu grepowania wszystkich tras związanych z „api”:

php bin/console d:r | grep --line-buffered "api"
Nicolas Bonnici
źródło
Jedyne rozwiązanie, które działa, jeśli twoja_komenda jest długotrwała (tak jak tail -f some.logw moim przypadku) ...
Izkata
0

Często kolejność przetwarzania nie ma znaczenia. GNU Parallel jest stworzone dla tej sytuacji:

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

Jeśli przetwarzanie bardziej przypomina:

grep xyz abc.txt | myprogram_reading_from_stdin

i myprogramjest powolny, możesz biec:

grep xyz abc.txt | parallel --pipe myprogram_reading_from_stdin
Ole Tange
źródło
0

Powtarzaj wyniki grep za pomocą pętli while / read. Lubić:

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

Dla tych, którzy szukają jednowierszowego:

grep xyz abc.txt | while read -r line; do echo "Processing $line"; done
laurent
źródło