Poniżej znajduje się tekst w pliku:
Pseudo name=Apple Code=42B state=fault Pseudo name=Prance Code=43B state=good
Muszę grepować „42B” i uzyskaj wynik z powyższego tekstu, taki jak:
Pseudo name=Apple Code=42B state=fault
Czy ktoś ma pomysł, jak to osiągnąć za pomocą grep
/ awk
/ sed
?
Komentarze
- Oznaczyłeś to pytanie tylko tagiem " grep ". Czy zatem szukasz tylko rozwiązań " grep "? W pytaniu określasz również awk & sed. Czy możemy dodać te tagi? Nie byłem ' pewien twojego zamiaru, kiedy edytowałem pytanie zeszłej nocy.
- stackoverflow.com/ pytania / 12024410 / …
Odpowiedź
Z awk
awk -v RS="" "/42B/" file
RS=
zmienia separator rekordów wejściowych od nowej linii do pustych linii. Jeśli którekolwiek pole w rekordzie zawiera /42B/
, wydrukuj rekord.
""
(ciąg pusty) to magia wartość używana do reprezentowania pustych linii zgodnie z POSIX :
Jeśli RS ma wartość null, a następnie rekordy są oddzielone sekwencjami składającymi się z
<newline>
plus co najmniej jednego pustego wiersze, początkowe lub końcowe puste wiersze nie powodują pustych rekordów na początku ani na końcu danych wejściowych, a<newline>
zawsze będzie separatorem pól, bez względu na wartość FS is.
Wyjściowe akapity nie będą być oddzielone, ponieważ separatorem wyjściowym pozostaje pojedynczy znak nowej linii. Aby upewnić się, że między wyjściowymi akapitami jest pusty wiersz, ustaw separator rekordu wyjściowego na dwie nowe linie:
awk -v RS="" -v ORS="\n\n" "/42B/" file
Komentarze
- +1 za eleganckie rozwiązanie. Nie ' nie musisz jednak przekierowywać pliku …
- palce były na autopilocie.
- @jasonwryan, chyba że potrzebujesz dostępu do nazwy pliku w awk (
FILENAME
), ' nie jest złym pomysłem użycie przekierowania, ponieważ pozwala to uniknąć problemów z nazwą pliku zawierającą=
lub zaczynające się od-
(lub-
), zapewnia spójne komunikaty o błędach oraz unika uruchamianiaawk
lub wykonywania innych przekierowań, jeśli plik wejściowy nie może ' t zostać otwarty.
Odpowiedź
Zakładając, że dane są ustrukturyzowane w taki sposób, że zawsze jest to linia przed i po, którą chcesz, możesz użyć grep „s -A
(po) i -B
(przed) przełącza się, aby poinformować go, aby zawierał 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, aby tę samą liczbę wierszy przed i po wyszukiwanym haśle możesz użyć przełącznika -C
(kontekst):
$ grep -C 1 "42B" sample.txt Pseudo name=Apple Code=42B state=fault
Jeśli chcesz być bardziej rygorystyczny przy dopasowywaniu wielu wierszy, możesz użyć narzędzia pcregrep
, aby dopasować wzorzec w wielu wierszach:
$ pcregrep -M "Pseudo.*\n.*42B.*\nstate.*" sample.txt Pseudo name=Apple Code=42B state=fault
Powyższy wzorzec pasuje w następujący sposób:
-
-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ępują dowolne znaki aż do końca wiersza\n
, po którym następują dowolne znaki aż do ciągu"42B"
, po którym następują dowolne znaki aż do kolejny koniec wiersza (\n
), po którym następuje ciąg"state"
, po którym następują dowolne znaki.
Komentarze
- (kontekst) może służyć jako skrót, jeśli
-A
i-B
są takie same. - @DavidBaggerman – dzięki. Dodałem go do odpowiedzi.
- Dlaczego ten jeden nie zagłosował? To odpowiada na pytanie.
Odpowiedź
grep
z niektóre odmiany Uniksa mają flagę -p
dla „akapitu”. Wiem, że AIX robi .
grep -p 42B <myfile>
zrobiłby dokładnie to, o co prosisz . YMMV i GNU grep nie mają tej flagi.
Komentarze
- Posiadanie flagi -p byłoby cudowne. Szczególnie jeśli jest używane razem z -v, więc możesz wykluczyć całe akapity z wyjścia.
Odpowiedź
Prawdopodobnie jest podobnie łatwy sposób na zrobienie tego w awk, ale w perlu:
cat file | perl -ne "BEGIN { $/="\n\n" }; print if $_ =~ /42B/;"
Oznacza to, że należy podzielić plik na fragmenty oddzielone pustymi wierszami, a następnie wydrukować tylko te fragmenty, które pasują do wyrażenia regularnego.
Komentarze
- Można to uprościć, używając opcji i skrótów oraz tracąc bezużyteczne użycie
cat
;perl -00 -ne 'print if /42B/' file
Odpowiedź
Inne rozwiązanie w Perlu, bez końcowa pusta linia:
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
Komentarze
- Lub krótsze (a przez to bardziej czytelne), jak triplee napisał w komentarzu:
perl -00 -ne 'print if /42B/' file
.