Jak grep dla tekstu w pliku i wyświetlić akapit z tekstem?

24

Poniżej znajduje się tekst w pliku:

Pseudo name=Apple
Code=42B
state=fault

Pseudo name=Prance
Code=43B
state=good

Muszę grep dla „42B” i uzyskać wynik z powyższego tekstu, takiego jak:

Pseudo name=Apple
Code=42B
state=fault

Czy ktoś ma pomysł, jak to osiągnąć za pomocą grep/ awk/ sed?

Jaya William
źródło
Oznaczono to pytanie tylko słowem „grep”. Szukasz więc tylko rozwiązań „grep”? W pytaniu podajesz także awk i sed. Czy możemy dodać te tagi? Nie byłem pewien twoich intencji, kiedy edytowałem pytanie zeszłej nocy.
slm
stackoverflow.com/questions/12024410/…
Ciro Santilli 新疆 改造 中心 法轮功 六四 事件

Odpowiedzi:

38

Z awk

awk -v RS='' '/42B/' file

RS=zmienia separator rekordów wejściowych z nowej linii na puste linie. Jeśli którekolwiek pole w rekordzie zawiera /42B/wydrukuj rekord.

''(ciąg zerowy) to magiczna wartość używana do reprezentowania pustych linii zgodnie z POSIX :

Jeśli RS ma wartość NULL, wówczas rekordy są oddzielane sekwencjami składającymi się z <newline>plus jednej lub więcej pustych linii, wiodące lub końcowe puste linie nie powinny powodować pustych rekordów na początku lub na końcu danych wejściowych, a a <newline>zawsze musi być separatorem pól, bez względu na wartość FS .

Akapity wyjściowe nie zostaną rozdzielone, ponieważ separator wyjściowy pozostaje pojedynczą nową linią. Aby upewnić się, że między akapitami wyjściowymi jest pusta linia, ustaw separator rekordów wyjściowych na dwie nowe linie:

awk -v RS='' -v ORS='\n\n' '/42B/' file
llua
źródło
1
+1 za eleganckie rozwiązanie. Nie musisz jednak przekierowywać pliku ...
jasonwryan
palce były na autopilocie.
llua
2
@jasonwryan, chyba że potrzebujesz dostępu do nazwy pliku w programie awk ( FILENAME), nie jest złym pomysłem stosowanie przekierowania, ponieważ pozwala to uniknąć problemów z nazwą pliku =lub jego początkiem -(lub istnieniem -), powoduje pojawienie się spójnych komunikatów o błędach i pozwala uniknąć uruchamiania awklub wykonywania inne przekierowania, jeśli nie można otworzyć pliku wejściowego.
Stéphane Chazelas
14

Zakładając, że dane mają taką strukturę, że zawsze jest to linia przed i po tym, możesz chcieć użyć przełączników grep -A(po) i -B(przed), aby powiedzieć mu, aby zawierała 1 linię przed dopasowaniem i 1 linię po niej:

$ grep -A 1 -B 1 "42B" sample.txt
Pseudo name=Apple
Code=42B
state=fault

Jeśli chcesz mieć te same linie liczb przed i po wyszukiwaniu, możesz użyć przełącznika -C(kontekstowego):

$ grep -C 1 "42B" sample.txt
Pseudo name=Apple
Code=42B
state=fault

Jeśli chcesz być bardziej rygorystyczny podczas dopasowywania wielu linii, możesz użyć narzędzia pcregrep, aby dopasować wzór do wielu linii:

$ pcregrep -M 'Pseudo.*\n.*42B.*\nstate.*' sample.txt
Pseudo name=Apple
Code=42B
state=fault

Powyższy wzór odpowiada następująco:

  • -M - wiele linii
  • 'Pseudo.*\n.*42B.*\nstate.*'- dopasowuje grupę ciągów, w których pierwszy ciąg zaczyna się od słowa, "Pseudo"po którym następuje dowolny znak do końca linii \n, następnie dowolne znaki do łańcucha, "42B"po którym następują dowolne znaki do następnego końca linii ( \n), a następnie ciąg "state"a następnie dowolne znaki.
slm
źródło
5
-C(kontekst) może być używany jako skrót, jeśli -Ai -Bsą takie same.
David Baggerman
@DavidBaggerman - dzięki. Dodano to do odpowiedzi.
slm
Dlaczego głosowanie na jeden głos? To odpowiada na pytanie.
slm
4

Prawdopodobnie istnieje podobnie podobny sposób na awk, ale w Perlu:

cat file | perl -ne 'BEGIN { $/="\n\n" }; print if $_ =~ /42B/;'

To w zasadzie mówi o podzieleniu pliku na części ograniczone pustymi liniami, a następnie wydrukowaniu tylko tych części, które pasują do wyrażenia regularnego.

HorsePunchKid
źródło
10
Można to uprościć, używając opcji i skrótów, i tracąc bezużyteczne użyciecat ; perl -00 -ne 'print if /42B/' file
tripleee
4

grepNiektórych smaków Unix mieć -pflagę „ust”. Wiem, że robi to AIX .

grep -p 42B <myfile>

zrobiłby dokładnie to, o co prosisz. YMMV i GNU grep nie mają tej flagi.

Morten
źródło
Posiadanie flagi -p byłoby cudowne. Zwłaszcza jeśli zostanie użyte razem z -v, aby można było wykluczyć całe akapity z wyników.
IllvilJa
2

Inne rozwiązanie perla, bez końcowej pustej linii:

perl -00ne 'if ($_ =~ /42B/) {chomp($_); printf "%s\n",$_}' foo

Przykład

% perl -00ne 'if ($_ =~ /42B/) {chomp($_); printf "%s\n",$_}' foo
Pseudo name=Apple
Code=42B
state=fault

% cat foo
Pseudo name=Apple
Code=42B
state=fault

Pseudo name=Prance
Code=43B
state=good
AB
źródło
1
Lub krócej (a zatem bardziej czytelny), jak triplee napisał w komentarzu: perl -00 -ne 'print if /42B/' file.
mivk