Het lijkt erop dat ik grep / egrep misbruik.

Ik probeerde te zoeken naar tekenreeksen in meerdere regels en kon geen overeenkomst vinden, terwijl ik weet dat wat ik zoek, overeen moet komen. Oorspronkelijk dacht ik dat mijn regexes verkeerd waren, maar ik las uiteindelijk dat deze tools werken per regel (ook mijn regexes waren zo triviaal dat het niet het probleem kon zijn).

Dus welke tool zou je gebruiken om patronen over meerdere regels te zoeken?

Opmerkingen

  • mogelijk duplicaat van Multiline patroonovereenkomst met sed, awk of grep
  • @CiroSantilli – Ik denk niet dat deze Q en de Q waarnaar je hebt gelinkt duplicaten zijn. De andere Q vraagt hoe je ‘ patronen kunt matchen met meerdere regels (dwz welk gereedschap moet / kan ik gebruiken om dit te doen) terwijl deze vraagt hoe je dit moet doen met grep. Ze zijn nauw verwant, maar geen dups, IMO.
  • @sim die gevallen zijn moeilijk te beslissen: ik begrijp uw punt. Ik denk dat dit specifieke geval beter is als een duplicaat omdat de gebruiker zei "grep" en suggereerde het werkwoord ” naar grep “, en de beste antwoorden, inclusief geaccepteerde, gebruik ‘ t grep.
  • Er is geen indicatie om aan te tonen dat hier een meerregelige reguliere expressie nodig is. Overweeg om een concreet voorbeeld te laten zien met invoergegevens en verwachte uitvoergegevens, evenals uw eerdere inspanningen.

Antwoord

Hier “sa sed een die je grep -achtig gedrag op meerdere regels geeft:

sed -n "/foo/{:start /bar/!{N;b start};/your_regex/p}" your_file 

Hoe het werkt

  • -n onderdrukt het standaardgedrag van het afdrukken van elke regel
  • /foo/{} geeft aan dat het overeenkomt met foo en doe wat binnen de squigglies komt naar de overeenkomende regels. Vervang foo door het begingedeelte van het patroon.
  • :start is een vertakkingslabel om ons te helpen door te gaan totdat we het einde van onze regex hebben gevonden.
  • /bar/!{} zal uitvoeren wat in de squigglies staat de regels die niet “overeenkomen met bar. Vervang met het eindgedeelte van het patroon.
  • N voegt de volgende regel toe aan de actieve buffer (sed noemt dit de patroonruimte)
  • b start zal onvoorwaardelijk vertakken naar het start label dat we hebben gemaakt eerder om de volgende regel te blijven toevoegen zolang de patroonruimte geen bar bevat.
  • /your_regex/p drukt de patroonruimte af als deze overeenkomt met your_regex. U moet your_regex vervangen door de hele uitdrukking die u over meerdere regels wilt matchen.

Opmerkingen

  • +1 Toevoegen aan de toolikt! Bedankt.
  • Opmerking: op MacOS geeft dit sed: 1: "/foo/{:start /bar/!{N;b ...": unexpected EOF (pending }'s)
  • sed: unterminated { foutmelding
  • @Nomaed Hier in het donker opgenomen, maar bevat je regex toevallig ” {” -tekens? Als dit het geval is, moet u ‘ ze backslashen en escapen.
  • @Nomaed Het lijkt erop dat het te maken heeft met de verschillen tussen sed implementaties. Ik heb geprobeerd de aanbevelingen in dat antwoord op te volgen om het bovenstaande script standaardcompatibel te maken, maar het vertelde me dat ” start ” een ongedefinieerde label. Dus ik ‘ m niet zeker of dit op een standaard-compatibele manier kan worden gedaan. Als het je lukt, aarzel dan niet om mijn antwoord te bewerken.

Antwoord

Ik gebruik meestal een tool genaamd pcregrep die kan worden geïnstalleerd in de meeste Linux-versies met yum of apt.

Voor bijv.

Stel dat u een bestand heeft met de naam testfile met inhoud

abc blah blah blah def blah blah blah 

U kunt het volgende commando uitvoeren:

$ pcregrep -M "abc.*(\n|.)*def" testfile 

om patroonovereenkomsten op meerdere regels uit te voeren.

Bovendien, u kunt hetzelfde doen met sed.

$ sed -e "/abc/,/def/!d" testfile 

Reacties

  • deze sed suggestie wordt overgeslagen de regel waar def zou worden gevonden

Antwoord

Gewoon een normale grep die Perl-regexp parameter P ondersteunt, zal dit werk doen.

$ echo "abc blah blah blah def blah blah blah" | grep -oPz "(?s)abc.*?def" abc blah blah blah def 

(?s) genaamd DOTALL modifier die ervoor zorgt dat de punt in je reguliere expressie niet alleen overeenkomt met de karakters maar ook met de regeleindes.

Reacties

  • Wanneer ik deze oplossing probeer, eindigt de uitvoer niet op ‘ def ‘ maar gaat naar het einde van het bestand ‘ blah ‘
  • misschien je grep ondersteunt geen -P optie
  • Dit was de enige die voor mij werkte – heb alle sed suggesties geprobeerd, maar ‘ ging niet zo ver als het installeren van grep-alternatieven.
  • $ grep --version: grep (GNU grep) 3.1 in de Windows Git Bash heeft een optie -P, --perl-regexp maar (?s) heeft geen ‘ t lijkt daar te werken. Het toont nog steeds alleen de eerste regel. Hetzelfde patroon met dezelfde testreeks werkt op regex101.com . Is er een alternatief in de Git Bash? sed? (sed (GNU sed) 4.8 hier)
  • Weet jij hoe je context aan de uitvoer moet toevoegen? grep -1 werkt hier niet ‘ t.

Antwoord

Hier “een eenvoudigere benadering met Perl:

perl -e "$f=join("",<>); print $& if $f=~/foo\nbar.*\n/m" file 

of (aangezien JosephR de sed route , ik “zal schaamteloos zijn suggestie ) stelen

perl -n000e "print $& while /^foo.*\nbar.*\n/mg" file 

### Uitleg

$f=join("",<>);: dit leest het volledige bestand en slaat de inhoud (nieuwe regels en alles) op in de variabele $f. We proberen vervolgens foo\nbar.*\n te matchen, en het af te drukken als het overeenkomt (de speciale variabele $& bevat de laatst gevonden overeenkomst). De ///m is nodig om de reguliere expressie op nieuwe regels te laten matchen.

De -0 stelt het scheidingsteken voor invoerrecords in. Als u dit instelt op 00, wordt de “alinea-modus” geactiveerd, waarbij Perl opeenvolgende nieuwe regels (\n\n) als recordscheidingsteken gebruikt. In gevallen waarin er geen opeenvolgende nieuwe regels zijn, wordt het volledige bestand in één keer gelezen (geslurpt).

### Waarschuwing: doe dit niet voor grote bestanden, het wordt geladen het hele bestand in het geheugen en dat kan een probleem zijn.

Opmerkingen

  • Ik don ‘ t weet veel over Perl, maar zou ‘ t het my $f=join("",<>); strikt genomen moeten zijn?
  • Alleen @Sapphire_Brick als je in strikte modus bent (use strict;). Het ‘ is een goede gewoonte om erin te komen, vooral bij het schrijven van grotere scripts, maar het ‘ is een overbodige luxe voor een kleine oneliner als deze één.

Antwoord

Stel dat we het bestand hebben test.txt met daarin:

blabla blabla foo here is the text to keep between the 2 patterns bar blabla blabla 

De volgende code kan worden gebruikt:

sed -n "/foo/,/bar/p" test.txt 

Voor de volgende uitvoer:

foo here is the text to keep between the 2 patterns bar 

Antwoord

Het grep-alternatief sift ondersteunt overeenkomsten tussen meerdere regels (disclaimer: ik ben de auteur).

Stel dat testfile bevat:

 <book> <title>Lorem Ipsum</title> <description>Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua</description> </book> 

sift -m '<description>.*?</description>' (toon de regels met de beschrijving)

Resultaat:

 testfile: <description>Lorem ipsum dolor sit amet, consectetur testfile: adipiscing elit, sed do eiusmod tempor incididunt ut testfile: labore et dolore magna aliqua</description> 

sift -m '<description>(.*?)</description>' --replace 'description="$1"' --no-filename (extract en formatteer de beschrijving opnieuw)

Resultaat:

description="Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua" 

Reacties

  • Zeer mooie tool. Gefeliciteerd! Probeer het op te nemen in distributies zoals Ubuntu.

Answer

Ik heb deze voor mij opgelost met grep en – Een optie met een andere grep.

grep first_line_word -A 1 testfile | grep second_line_word 

De -A 1 optie drukt 1 regel na de gevonden regel af. Het hangt natuurlijk af van uw bestand en woordcombinatie. Maar voor mij was het de snelste en betrouwbare oplossing.

Reacties

  • alias grepp = ‘ grep –color = auto -B10 -A20 -i ‘ en dan kat een bestand | grepp blah | grepp foo | grepp bar … ja die -A en -B zijn erg handig …je hebt het beste antwoord
  • Dit is niet ‘ t super deterministisch en het negeert het hele patroon om gewoon een andere enkele regel te krijgen (alleen gebaseerd op de nabijheid naar de eerste regel). Het ‘ is beter om het programma te vertellen hoe ver het ook moet gaan om tot een bepaald patroon te komen dat u ‘ absoluut zeker is het einde van de tekst die u ‘ probeert te matchen. Als bijvoorbeeld testfile zo wordt bijgewerkt dat second_line_word op de derde regel staat, dan mist u niet alleen de eerste regel (vanwege je tweede grep) maar je ‘ mist de regel die tussen de twee begon te verschijnen niet.
  • Dit zou een goed genoeg MO zijn voor ad-hocopdrachten waarbij je eigenlijk maar een enkele regel in de uitvoer wilt die je al begrijpt. Ik denk niet ‘ niet dat ‘ s is waar het OP naar op zoek is en je zou waarschijnlijk ook gewoon kunnen kopiëren / plakken op dat punt vanwege het is ad hoc.

Answer

Een manier om dit te doen is met Perl. bijv. hier is de inhoud van een bestand met de naam foo:

foo line 1 bar line 2 foo foo foo line 5 foo bar line 6 

Nu, hier is wat Perl die match met elke regel die begint met foo gevolgd door elke regel die begint met bar:

cat foo | perl -e "while(<>){$all .= $_} while($all =~ /^(foo[^\n]*\nbar[^\n]*\n)/m) { print $1; $all =~ s/^(foo[^\n]*\nbar[^\n]*\n)//m; }" 

De Perl, uitgesplitst:

  • while(<>){$all .= $_} Dit laadt de volledige standaardinvoer in de variabele $all
  • while($all =~ Terwijl de variabele all de reguliere expressie heeft …
  • /^(foo[^\n]*\nbar[^\n]*\n)/m De regex: foo aan het begin van de regel, gevolgd door een willekeurig aantal niet-nieuwe regel tekens, gevolgd door een nieuwe regel, onmiddellijk gevolgd door “bar”, en de rest van de regel met bar erin. /m aan het einde van de regex betekent “match over meerdere regels”
  • print $1 Druk het deel van de regex af dat tussen haakjes stond (in dit geval de volledige reguliere expressie)
  • s/^(foo[^\n]*\nbar[^\n]*\n)//m Wis de eerste overeenkomst voor de regex, zodat we meerdere gevallen van de regex kunnen matchen in het betreffende bestand

En de output:

foo line 1 bar line 2 foo bar line 6 

Opmerkingen

  • Kom langs om te zeggen dat uw Perl kan worden ingekort tot het meer idiomatische: perl -n0777E 'say $& while /^foo.*\nbar.*\n/mg' foo

Antwoord

Als we de tekst tussen de 2 patronen willen krijgen, met uitzondering van zichzelf.

Stel dat we het bestand test.txt met daarin:

blabla blabla foo here is the text to keep between the 2 patterns bar blabla blabla 

De volgende code kan worden gebruikt:

 sed -n "/foo/{ n b gotoloop :loop N :gotoloop /bar/!{ h b loop } /bar/{ g p } }" test.txt 

Voor de volgende uitvoer:

here is the text to keep between the 2 patterns 

Hoe werkt het, laten we maak het stap voor stap

  1. /foo/{ wordt geactiveerd wanneer regel “foo” bevat
  2. n vervang de patroonruimte door de volgende regel, dwz het woord “hier”
  3. b gotoloop vertakking naar het label “gotoloop”
  4. :gotoloop definieert het label “gotoloop”
  5. /bar/!{ als het patroon geen “bar” bevat
  6. h vervang de hold-spatie door patroon, dus “here” wordt opgeslagen in de hold-ruimte
  7. b loop vertakking naar het label “loop”
  8. :loop definieert het label “loop”
  9. N voegt het patroon toe aan de hold-ruimte.
    De hold-ruimte bevat nu:
    “hier”
    “is de”
  10. :gotoloop We zijn nu bij stap 4, en herhalen totdat een regel “bar” bevat
  11. /bar/ lus is voltooid, “bar” is gevonden, it ” s de patroonruimte
  12. patroonruimte is vervangen door hold-spatie die alle regels tussen” foo “en” bar “bevat die zijn opgeslagen tijdens de hoofdlus
  13. p kopieer patroonruimte naar standaarduitvoer

Klaar!

Reacties

  • Goed gedaan, +1. Ik gebruik deze commandos meestal niet door tr ‘ de nieuwe regels in SOH te zetten en normale sed-opdrachten uit te voeren en de nieuwe regels te vervangen.

Geef een reactie

Het e-mailadres wordt niet gepubliceerd. Vereiste velden zijn gemarkeerd met *