Mai jos este textul din fișier:
Pseudo name=Apple Code=42B state=fault Pseudo name=Prance Code=43B state=good
Trebuie să grep pentru „42B” și obțineți rezultatul din textul de mai sus, cum ar fi:
Pseudo name=Apple Code=42B state=fault
Are cineva idee despre cum să realizăm acest lucru folosind grep
/ awk
/ sed
?
Comentarii
- Ați etichetat această întrebare doar cu " grep ". Căutați doar soluții " grep " atunci? În întrebare specificați și awk & sed. Putem adăuga aceste etichete? Nu eram ' sigur de intenția ta când am editat întrebarea aseară.
- stackoverflow.com/ questions / 12024410 / …
Răspuns
Cu awk
awk -v RS="" "/42B/" file
RS=
modifică separatorul de înregistrări de intrare de la o linie nouă la linii goale. Dacă vreun câmp dintr-o înregistrare conține /42B/
tipăriți înregistrarea.
""
(șirul nul) este o magie valoare utilizată pentru a reprezenta liniile goale conform POSIX :
Dacă RS este nul, apoi înregistrările sunt separate prin secvențe formate dintr-un
<newline>
plus unul sau mai multe goluri liniile, liniile goale care conduc sau nu sunt rezultate în înregistrări goale la începutul sau la sfârșitul intrării și un<newline>
trebuie să fie întotdeauna un separator de câmp, indiferent de valoarea FS este.
Paragrafele de ieșire nu vor să fie separate deoarece separatorul de ieșire rămâne o singură linie nouă. Pentru a vă asigura că există o linie goală între paragrafele de ieșire, setați separatorul de înregistrări de ieșire la două linii noi:
awk -v RS="" -v ORS="\n\n" "/42B/" file
Comentarii
- +1 pentru o soluție elegantă. ' nu trebuie să redirecționați fișierul deși …
- degetele erau pe pilot automat.
- @jasonwryan, cu excepția cazului în care aveți nevoie de acces la numele fișierului din awk (
FILENAME
), ' nu este o idee rea să folosiți redirecționarea, deoarece evită probleme pentru numele fișierului care conține=
sau începând cu-
(sau fiind-
), face ca mesajele de eroare să fie consistente și evită rulareaawk
sau efectuarea altor redirecționări dacă fișierul de intrare nu poate fi ' deschis.
Răspuns
Presupunând că datele sunt structurate astfel încât să fie întotdeauna linia înainte și după aceea pe care o doriți, puteți folosi grep „s -A
(după) și -B
(înainte) comută pentru a-i spune să includă 1 linie înainte de meci și 1 linie după aceasta:
$ grep -A 1 -B 1 "42B" sample.txt Pseudo name=Apple Code=42B state=fault
Dacă doriți ca același număr de linii înainte și după termenul de căutare puteți utiliza comutatorul -C
(context):
$ grep -C 1 "42B" sample.txt Pseudo name=Apple Code=42B state=fault
Dacă „doriți să fiți mai stricți atunci când potriviți mai multe linii, puteți utiliza instrumentul pcregrep
, pentru a se potrivi model peste mai multe linii:
$ pcregrep -M "Pseudo.*\n.*42B.*\nstate.*" sample.txt Pseudo name=Apple Code=42B state=fault
Modelul de mai sus se potrivește după cum urmează:
-
-M
– mai multe linii -
"Pseudo.*\n.*42B.*\nstate.*"
– se potrivește cu un grup de șiruri în care primul șir începe cu cuvântul"Pseudo"
urmat de orice caractere până la sfârșitul liniei\n
, urmat de orice caractere până până la șirul"42B"
urmat de orice caractere până un alt capăt de linie (\n
), urmat de șirul"state"
urmat de orice caractere.
Comentarii
-
-C
(context) poate fi folosit ca o comandă rapidă, dacă-A
și-B
sunt aceleași. - @DavidBaggerman – mulțumesc. L-am adăugat la răspuns.
- De ce votul negativ? Aceasta răspunde la întrebare.
Răspunde
grep
din unele arome ale Unix au semnalul -p
pentru „paragraf”. Știu că face AIX .
grep -p 42B <myfile>
ar face exact ceea ce cereți acolo . YMMV și GNU grep nu au acest semnal.
Comentarii
- A avea semnalul -p ar fi minunat. Mai ales dacă este utilizat împreună cu -v, astfel încât să puteți exclude paragrafe întregi din ieșire.
Răspuns
Există probabil o modalitate ușor similară de a face acest lucru cu awk, dar în perl:
cat file | perl -ne "BEGIN { $/="\n\n" }; print if $_ =~ /42B/;"
Asta spune practic să împărțiți fișierul în bucăți delimitate de linii goale, apoi să imprimați doar acele bucăți care se potrivesc cu expresia dvs. obișnuită. Comentarii
- Acest lucru poate fi simplificat utilizând opțiuni și stenografii și pierzând utilizarea inutilă a
cat
;perl -00 -ne 'print if /42B/' file
Răspuns
O altă soluție perl, fără o linie goală finală:
perl -00ne "if ($_ =~ /42B/) {chomp($_); printf "%s\n",$_}" foo
Exemplu
% 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
Comentarii
- Sau mai scurte (și, prin urmare, mai lizibile), după cum a scris triplee într-un comentariu:
perl -00 -ne 'print if /42B/' file
.