Nedenfor er teksten i filen:
Pseudo name=Apple Code=42B state=fault Pseudo name=Prance Code=43B state=good
Jeg skal grep for “42B” og få output fra ovenstående tekst som:
Pseudo name=Apple Code=42B state=fault
Har nogen idé om, hvordan man opnår dette ved hjælp af grep
/ awk
/ sed
?
Kommentarer
- Du taggede dette spørgsmål med bare " grep ". Leder du kun efter " grep " løsninger da? I spørgsmålet angiver du awk & sed også. Kan vi tilføje disse tags? Jeg var ikke ' ikke sikker på din hensigt, da jeg redigerede spørgsmålet i går aftes.
- stackoverflow.com/ spørgsmål / 12024410 / …
Svar
Med awk
awk -v RS="" "/42B/" file
RS=
ændres input-record separator fra en ny linje til tomme linjer. Hvis et felt i en post indeholder /42B/
skal du udskrive posten.
""
(nullstrengen) er en magi værdi, der bruges til at repræsentere blanke linjer i henhold til POSIX :
Hvis RS er nul, derefter adskilles poster med sekvenser bestående af en
<newline>
plus en eller flere tomme linjer, ledende eller efterfølgende tomme linjer må ikke resultere i tomme poster i begyndelsen eller slutningen af input, og en<newline>
skal altid være en feltadskiller, uanset hvilken værdi FS er.
Outputafsnittene vil ikke være adskilt, da udgangsseparatoren forbliver en enkelt ny linje. For at sikre, at der er en tom linje mellem outputafsnit, skal du indstille udgangspostseparatoren til to nye linjer:
awk -v RS="" -v ORS="\n\n" "/42B/" file
Kommentarer
- +1 for en elegant løsning. Du behøver ikke ' ikke omdirigere filen …
- fingrene var på autopilot.
- @jasonwryan, medmindre du har brug for adgang til filnavnet inden for awk (
FILENAME
), er det ' ikke en dårlig idé at bruge omdirigering, da det undgår problemer for filnavne, der indeholder=
eller starter med-
(eller er-
), giver ensartede fejlmeddelelser og undgår at køreawk
eller udføre andre omdirigeringer, hvis inputfilen ikke kan ' åbnes.
Svar
Under forudsætning af, at dataene er struktureret, så det altid er linjen før og efter det, du ønsker, kan du bruge grep “s -A
(efter) og -B
(før) skifter for at fortælle det at inkludere 1 linje før kampen og 1 linje efter den:
$ grep -A 1 -B 1 "42B" sample.txt Pseudo name=Apple Code=42B state=fault
Hvis du vil have samme nummerlinjer før og efter søgeudtrykket kan du bruge -C
(kontekst) switch:
$ grep -C 1 "42B" sample.txt Pseudo name=Apple Code=42B state=fault
Hvis du “gerne vil være strengere, når du matcher de flere linjer, kan du bruge værktøjet pcregrep
for at matche en mønster over flere linjer:
$ pcregrep -M "Pseudo.*\n.*42B.*\nstate.*" sample.txt Pseudo name=Apple Code=42B state=fault
Ovenstående mønster matcher som følger:
-
-M
– flere linjer -
"Pseudo.*\n.*42B.*\nstate.*"
– matcher en gruppe strenge, hvor den første streng starter med ordet"Pseudo"
efterfulgt af eventuelle tegn op til slutningen af linjen\n
, efterfulgt af alle tegn op indtil strengen"42B"
efterfulgt af eventuelle tegn indtil en anden ende af linjen (\n
) efterfulgt af strengen"state"
efterfulgt af eventuelle tegn.
Kommentarer
Svar
grep
nogle varianter af Unix har -p
flag for “afsnit”. Jeg ved, at AIX gør .
grep -p 42B <myfile>
ville gøre præcis, hvad du beder om der YMMV og GNU grep har ikke dette flag.
Kommentarer
- At have -p-flag ville være vidunderligt. Især hvis det bruges sammen med -v, så du kan udelukke hele afsnit fra output.
Svar
Der er sandsynligvis en lignende nem måde at gøre det med awk, men i perl:
cat file | perl -ne "BEGIN { $/="\n\n" }; print if $_ =~ /42B/;"
Det siger grundlæggende at opdele filen i klumper afgrænset af tomme linjer og derefter kun udskrive de klumper, der matcher dit regulære udtryk.
Kommentarer
- Dette kan forenkles ved at bruge indstillinger og stenografi og miste ubrugelig brug af
cat
;perl -00 -ne 'print if /42B/' file
Svar
En anden perl-løsning uden en efterfølgende tom linje:
perl -00ne "if ($_ =~ /42B/) {chomp($_); printf "%s\n",$_}" foo
Eksempel
% 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 kortere (og dermed mere læsbar), som triplee skrev i en kommentar:
perl -00 -ne 'print if /42B/' file
.
-C
(kontekst) kan bruges som en genvej, hvis-A
og-B
er de samme.