Nedan är texten i filen:
Pseudo name=Apple Code=42B state=fault Pseudo name=Prance Code=43B state=good
Jag behöver grep för ”42B” och få utdata från ovanstående text som:
Pseudo name=Apple Code=42B state=fault
Har någon idé om hur man uppnår detta med grep
/ awk
/ sed
?
Kommentarer
- Du taggade den här frågan med bara " grep ". Letar du bara efter " grep " lösningar då? I frågan anger du awk & sed också. Kan vi lägga till dessa taggar? Jag var inte ' inte säker på din avsikt när jag redigerade frågan igår kväll.
- stackoverflow.com/ frågor / 12024410 / …
Svar
Med awk
awk -v RS="" "/42B/" file
RS=
ändras ingångspostseparatorn från en ny rad till tomma rader. Om något fält i en post innehåller /42B/
skriv ut posten.
""
(nullsträngen) är en magi värde som används för att representera tomma rader enligt POSIX :
Om RS är noll, sedan separeras poster med sekvenser som består av en
<newline>
plus en eller flera tomma rader, ledande eller efterföljande tomma rader får inte leda till tomma poster i början eller slutet av inmatningen, och en<newline>
ska alltid vara en fältseparator, oavsett värdet på FS är.
Utgångsstyckena kommer inte separeras eftersom utmatningsseparatorn förblir en enda ny rad. För att säkerställa att det finns en tom rad mellan utgångsstycken ställer du in utgångspostavgränsaren på två nya rader:
awk -v RS="" -v ORS="\n\n" "/42B/" file
Kommentarer
- +1 för en elegant lösning. Du behöver dock inte ' omdirigera filen …
- fingrarna var på autopiloten.
- @jasonwryan, såvida du inte behöver åtkomst till filnamnet inom awk (
FILENAME
), det ' är inte en dålig idé att använda omdirigering eftersom det undviker problem för filnamn som innehåller=
eller börjar med-
(eller är-
), ger konsekventa felmeddelanden och undviker att köraawk
eller utföra andra omdirigeringar om ingångsfilen ' inte kan öppnas.
Svar
Förutsatt att data är strukturerade så att det alltid är raden före och efter det du vill kan du använda grep ”s -A
(efter) och -B
(före) växlar för att säga att den ska inkludera 1 rad före matchen och 1 rad efter den:
$ grep -A 1 -B 1 "42B" sample.txt Pseudo name=Apple Code=42B state=fault
Om du vill ha samma antal rader före och efter söktermen kan du använda -C
(context) -omkopplaren:
$ grep -C 1 "42B" sample.txt Pseudo name=Apple Code=42B state=fault
Om du vill vara strängare när du matchar flera rader kan du använda verktyget pcregrep
, för att matcha en mönster över flera rader:
$ pcregrep -M "Pseudo.*\n.*42B.*\nstate.*" sample.txt Pseudo name=Apple Code=42B state=fault
Mönstret ovan matchar enligt följande:
-
-M
– flera rader -
"Pseudo.*\n.*42B.*\nstate.*"
– matchar en grupp strängar där den första strängen börjar med ordet"Pseudo"
följt av alla tecken fram till slutet av raden\n
, följt av alla tecken upp till strängen"42B"
följt av några tecken fram till en annan ände av raden (\n
), följt av strängen"state"
följt av alla tecken.
Kommentarer
Svar
grep
vissa Unix-smaker har -p
-flaggan för ”avsnitt”. Jag vet att AIX gör .
grep -p 42B <myfile>
skulle göra precis vad du ber om där YMMV och GNU grep har inte den här flaggan.
Kommentarer
- Att ha flaggan -p skulle vara underbart. Speciellt om de används tillsammans med -v så kan du utesluta hela stycken från utdata.
Svar
Det finns förmodligen ett liknande enkelt sätt att göra det med awk, men i perl:
cat file | perl -ne "BEGIN { $/="\n\n" }; print if $_ =~ /42B/;"
Det säger i princip att dela upp filen i bitar avgränsade med tomma rader, för att bara skriva ut de bitar som matchar ditt vanliga uttryck.
Kommentarer
- Detta kan förenklas genom att använda alternativ och förkortningar och förlora värdelös användning av
cat
;perl -00 -ne 'print if /42B/' file
Svar
En annan perl-lösning, utan en tom rad:
perl -00ne "if ($_ =~ /42B/) {chomp($_); printf "%s\n",$_}" foo
Exempel
% 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
Kommentarer
- Eller kortare (och därmed mer läsbar), som triplee skrev i en kommentar:
perl -00 -ne 'print if /42B/' file
.
-C
(context) kan användas som en genväg, om-A
och-B
är desamma.