Hieronder staat de tekst in het bestand:
Pseudo name=Apple Code=42B state=fault Pseudo name=Prance Code=43B state=good
Ik moet grepen voor “42B” en haal de uitvoer uit de bovenstaande tekst zoals:
Pseudo name=Apple Code=42B state=fault
Heeft iemand een idee hoe dit te bereiken met grep
/ awk
/ sed
?
Reacties
- Je hebt deze vraag getagd met slechts " grep ". Bent u dan alleen op zoek naar " grep " oplossingen? In de vraag specificeer je ook awk & sed. Kunnen we die tags toevoegen? Ik was niet ' zeker van je bedoeling toen ik de vraag gisteravond bewerkte.
- stackoverflow.com/ vragen / 12024410 / …
Antwoord
Met awk
awk -v RS="" "/42B/" file
RS=
verandert het scheidingsteken voor invoerrecords van een nieuwe regel naar lege regels. Als een veld in een record /42B/
bevat, druk dan het record af.
""
(de null-reeks) is een magisch waarde die wordt gebruikt om lege regels weer te geven volgens POSIX :
If RS is null, en de records worden gescheiden door reeksen bestaande uit een
<newline>
plus een of meer blanco regels, leidende of achterliggende lege regels mogen niet resulteren in lege records aan het begin of einde van de invoer, en een<newline>
moet altijd een veldscheidingsteken zijn, ongeacht de waarde van FS is.
De uitvoerparagrafen zullen niet gescheiden worden aangezien het uitvoerscheidingsteken een enkele nieuwe regel blijft. Om er zeker van te zijn dat er een lege regel tussen de uitvoerparagrafen staat, stelt u het scheidingsteken voor uitvoerrecords in op twee nieuwe regels:
awk -v RS="" -v ORS="\n\n" "/42B/" file
Opmerkingen
- +1 voor een elegante oplossing. Je hoeft het bestand echter niet ' om te leiden …
- vingers bevonden zich op de automatische piloot.
- @jasonwryan, tenzij je toegang nodig hebt naar de bestandsnaam binnen awk (
FILENAME
), is het ' geen slecht idee om omleiding te gebruiken, aangezien dat problemen voorkomt voor bestandsnamen die=
of beginnend met-
(of-
), zorgt voor consistente foutmeldingen, en vermijdt het uitvoeren vanawk
of het uitvoeren van andere omleidingen als het invoerbestand ' niet kan worden geopend.
Answer
Ervan uitgaande dat de gegevens zo zijn gestructureerd dat het altijd de regel ervoor en daarna is die u wilt, kunt u gebruik maken van grep “s -A
(after) en -B
(before) schakelt om om de 1 regel voor de match en 1 regel erna op te nemen:
$ grep -A 1 -B 1 "42B" sample.txt Pseudo name=Apple Code=42B state=fault
Als u de dezelfde nummerregels voor en na de zoekterm kunt u de -C
(context) schakelaar gebruiken:
$ grep -C 1 "42B" sample.txt Pseudo name=Apple Code=42B state=fault
Als u “zou graag strenger willen zijn bij het matchen van de meerdere regels, u kunt de tool pcregrep
gebruiken om een patroon over meerdere regels:
$ pcregrep -M "Pseudo.*\n.*42B.*\nstate.*" sample.txt Pseudo name=Apple Code=42B state=fault
Het bovenstaande patroon komt als volgt overeen:
-
-M
– meerdere regels -
"Pseudo.*\n.*42B.*\nstate.*"
– komt overeen met een groep strings waarbij de eerste string begint met het woord"Pseudo"
gevolgd door willekeurige tekens tot het einde van de regel\n
, gevolgd door willekeurige tekens tot de tekenreeks"42B"
gevolgd door willekeurige tekens tot een ander einde van de regel (\n
), gevolgd door de tekenreeks"state"
gevolgd door willekeurige tekens.
Opmerkingen
-
-C
(context) kan als een snelkoppeling worden gebruikt, als-A
en-B
hetzelfde zijn. - @DavidBaggerman – bedankt. Toegevoegd aan het antwoord.
- Waarom stemmen we neer? Dit beantwoordt de vraag.
Antwoord
De grep
van sommige smaken van Unix hebben de -p
vlag voor “paragraaf”. Ik weet dat AIX doet .
grep -p 42B <myfile>
zou precies doen wat je daar vraagt . YMMV en GNU grep hebben deze vlag niet.
Opmerkingen
- De vlag -p hebben zou geweldig zijn. Vooral als het samen met -v wordt gebruikt, zodat u hele alineas van de uitvoer kunt uitsluiten.
Answer
Er is waarschijnlijk een vergelijkbare gemakkelijke manier om het te doen met awk, maar in perl:
cat file | perl -ne "BEGIN { $/="\n\n" }; print if $_ =~ /42B/;"
Dat betekent in feite dat het bestand moet worden opgesplitst in stukken die worden gescheiden door lege regels, en vervolgens alleen die stukken moeten afdrukken die overeenkomen met uw reguliere expressie.
Opmerkingen
- Dit kan worden vereenvoudigd door opties en afkortingen te gebruiken en het nutteloze gebruik van ;
perl -00 -ne 'print if /42B/' file
Antwoord
Een andere perl-oplossing, zonder een lege regel achteraan:
perl -00ne "if ($_ =~ /42B/) {chomp($_); printf "%s\n",$_}" foo
Voorbeeld
% 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
Opmerkingen
- Of korter (en dus beter leesbaar), zoals triplee schreef in een opmerking:
perl -00 -ne 'print if /42B/' file
.