Det verkar som att jag missbrukar grep / egrep.

Jag försökte söka efter strängar i flera rader och kunde inte hitta en matchning medan jag vet att det jag letar efter skulle matcha. Ursprungligen trodde jag att mina regexer var fel men så småningom läste jag att dessa verktyg fungerar per rad (även mina regexer var så triviala att det inte kunde vara problemet).

Så vilket verktyg skulle man använda för att söka mönster över flera rader?

Kommentarer

  • möjlig duplikat av Multiline mönstermatchning med sed, awk eller grep
  • @CiroSantilli – Jag tror inte att den här Q och den du länkade till är dubbletter. Den andra Q frågar hur du ’ d matchar mönster med flera rader (dvs. vilket verktyg ska / kan jag använder för att göra detta) medan den här frågar hur man gör det med grep. De är nära besläktade men inte dups, IMO.
  • @sim dessa fall är svårt att bestämma: Jag kan se er poäng. Jag tror att det här fallet är bättre som en duplikat, eftersom se användaren sa "grep" föreslår verbet ” till grep ”, och svar på topp, inklusive accepterat, använd inte ’ grep.
  • Det finns ingen indikation som visar att ett regelbundet uttryck med flera rader behövs här. Överväg att visa ett faktiskt exempel med indata och förväntad utdata samt ditt tidigare arbete.

Svar

Här ”sa sed en som ger dig grep -liknande beteende över flera rader:

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

Så fungerar det

  • -n undertrycker standardbeteendet för att skriva ut varje rad
  • /foo/{} instruerar att den ska matcha foo och gör vad som kommer inuti squiggliesna till de matchande raderna. Byt ut foo med den inledande delen av mönstret.
  • :start är en förgreningsetikett som hjälper oss att fortsätta att slingra tills vi hittar slutet på vår regex.
  • /bar/!{} kommer att utföra vad som finns i squigglies till raderna som inte matchar bar. Ersätt med slutet av mönstret.
  • N lägger till nästa rad till den aktiva bufferten (sed kallar detta mönsterutrymmet)
  • b start förgrenar sig villkorslöst till start -etiketten tidigare för att fortsätta lägga till nästa rad så länge som mönsterutrymmet inte innehåller bar.
  • /your_regex/p skriver ut mönsterutrymmet om det matchar your_regex. Du bör ersätta your_regex med hela uttrycket du vill matcha över flera rader.

Kommentarer

  • +1 Lägga till detta i toolikt! Tack.
  • Obs! På MacOS ger detta sed: 1: "/foo/{:start /bar/!{N;b ...": unexpected EOF (pending }'s)
  • Att få sed: unterminated { fel
  • @Nomaed Skott i mörkret här, men innehåller din regex någon ” {” tecken? Om så är fallet måste du ’ slå tillbaka dem.
  • @Nomaed Det verkar som om det har att göra med skillnaderna mellan sed implementeringar. Jag försökte följa rekommendationerna i det svaret för att göra ovanstående skript standardkompatibelt men det berättade för mig att ” start ” var en odefinierad märka. Så jag ’ är inte säker på om detta kan göras på ett standardkompatibelt sätt. Om du klarar det är du välkommen att redigera mitt svar.

Svar

Jag använder vanligtvis ett verktyg kallas pcregrep som kan installeras i större delen av linux-smaken med yum eller apt.

Till exempel.

Anta om du har en fil med namnet testfile med innehåll

abc blah blah blah def blah blah blah 

Du kan köra följande kommando:

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

för att göra mönstermatchning över flera rader.

Dessutom, du kan göra detsamma med sed också.

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

Kommentarer

  • detta sed förslaget hoppar raden där def skulle hittas

Svar

Helt enkelt en normal grep som stöder Perl-regexp parameter P gör det här jobbet.

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

(?s) kallas DOTALL-modifierare vilket gör att punkten i din regex inte bara matchar tecknen utan också radbrytningarna.

Kommentarer

  • När jag försöker med den här lösningen slutar inte utdata på ’ def ’ men går till slutet av filen ’ blah ’
  • kanske din grep stöder inte -P -alternativ
  • Detta var det enda som fungerade för mig – försökte alla sed förslag, men gick inte ’ så långt som att installera grep-alternativ.
  • $ grep --version: grep (GNU grep) 3.1 i Windows Git Bash har ett alternativ -P, --perl-regexp men (?s) inte ’ verkar inte fungera där. Den visar fortfarande endast första raden. Samma mönster med samma teststräng fungerar på regex101.com . Finns det ett alternativ i Git Bash? sed? (sed (GNU sed) 4.8 här)
  • Vet du hur du lägger till kontext till utgången? grep -1 fungerar inte ’ här.

Svar

Här ”är en enklare metod med Perl:

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

eller (eftersom JosephR tog sed rutt , jag stjäl skamlöst hans förslag )

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

### Förklaring

$f=join("",<>);: detta läser hela filen och sparar dess innehåll (nya rader och allt) i variabeln $f. Vi försöker sedan matcha foo\nbar.*\n och skriva ut det om det matchar (specialvariabeln $& innehåller den senaste matchningen som hittades). ///m behövs för att det reguljära uttrycket ska matcha över nya rader.

-0 ställer in ingångspostseparatorn. Om du ställer in detta på 00 aktiveras ”avsnittsläge” där Perl kommer att använda efterföljande nya rader (\n\n) som postavgränsare. I fall där det inte finns några nya rader i följd, läses hela filen (slurpas) på en gång.

### Varning: Gör inte detta för stora filer, det laddas hela filen i minnet och det kan vara ett problem.

Kommentarer

  • Jag don ’ t vet mycket om Perl, men skulle ’ inte behöva vara my $f=join("",<>);, strikt taget?
  • endast @Sapphire_Brick om du är i strikt läge (use strict;). Det ’ är en bra vana att komma in i, särskilt när man skriver större manus, men det ’ s överdöd för en liten enfodral som denna en.

Svar

Antag att vi har filen test.txt innehållande:

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

Följande kod kan användas:

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

För följande utdata:

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

Svar

Grep-alternativet sift stöder multilinjematchning (ansvarsfriskrivning: Jag är författaren).

Antag att testfile innehåller:

 <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>' (visa raderna som innehåller beskrivningen)

Resultat:

 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 (extrahera och formatera beskrivningen)

Resultat:

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

Kommentarer

  • Mycket trevligt verktyg. Grattis! Försök att inkludera det i distributioner som Ubuntu.

Svar

Jag löste den här för mig med grep och – Ett alternativ med en annan grep.

grep first_line_word -A 1 testfile | grep second_line_word 

Alternativet -A 1 skriver ut en rad efter den hittade raden. Naturligtvis beror det på din fil- och ordkombination. Men för mig var det den snabbaste och pålitligaste lösningen.

Kommentarer

  • alias grepp = ’ grep –färg = auto -B10 -A20 -i ’ sedan kattfil | grepp bla | grepp foo | grepp bar … ja de -A och -B är väldigt praktiska …du har det bästa svaret
  • Det här är ’ t superdeterministiskt och det ignorerar hela mönstret till förmån för att bara få en annan enstaka rad (bara baserat på dess närhet till första raden). Det ’ är bättre att säga till programmet att gå hur långt det än måste gå för att komma till ett slags mönster du ’ helt säker är slutet på texten som du ’ försöker matcha. Till exempel om testfile uppdateras så att second_line_word står på tredje raden, saknar du inte bara den första raden (på grund av din andra grep) men du ’ saknar inte raden som började visas mellan de två.
  • Detta skulle vara tillräckligt bra MO för ad hoc-kommandon där du verkligen bara vill ha en enda rad i utdata som du redan förstod. Jag tror inte ’ att ’ är vad OP är efter och du kan antagligen också bara kopiera / klistra in vid den tidpunkten på grund av det är ad hoc.

Svar

Ett sätt att göra detta är med Perl. t.ex. här är innehållet i en fil med namnet foo:

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

Nu, här är lite Perl som kommer matcha alla rader som börjar med foo följt av alla rader som börjar med 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; }" 

Perl, uppdelad:

  • while(<>){$all .= $_} Detta laddar in hela standardingången till variabeln $all
  • while($all =~ Medan variabeln all har det reguljära uttrycket …
  • /^(foo[^\n]*\nbar[^\n]*\n)/m Regex: foo i början av raden, följt av valfritt antal icke-nylinjiga tecken, följt av en ny rad, följt omedelbart av ”stapel” och resten av raden med stapel i den. /m i slutet av regex betyder ”matchning över flera rader”
  • print $1 Skriv ut delen av regex som var inom parentes (i det här fallet hela reguljäruttrycket)
  • s/^(foo[^\n]*\nbar[^\n]*\n)//m Radera den första matchningen för regex, så att vi kan matcha flera fall av regex i filen i fråga

Och utdata:

foo line 1 bar line 2 foo bar line 6 

Kommentarer

  • Kom bara in för att säga att din Perl kan förkortas till det mer idiomatiska: perl -n0777E 'say $& while /^foo.*\nbar.*\n/mg' foo

Svar

Om vi vill få texten mellan de två mönstren exklusive sig själva.

Antag att vi har filen test.txt som innehåller:

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

Följande kod kan användas:

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

För följande utdata:

here is the text to keep between the 2 patterns 

Hur fungerar det, låt ”s gör det steg för steg

  1. /foo/{ utlöses när raden innehåller ”foo”
  2. n byt ut mönsterutrymmet med nästa rad, dvs. ordet ”här”
  3. b gotoloop gren till etiketten ”gotoloop”
  4. :gotoloop definierar etiketten ”gotoloop”
  5. /bar/!{ om mönstret inte innehåller ”bar”
  6. h ersätt hållutrymmet med mönster, så ”här” sparas i hållutrymmet
  7. b loop gren till etiketten ”loop”
  8. :loop definierar etiketten ”loop”
  9. N lägger till mönstret i hållutrymmet.
    Nu innehåller utrymmet:
    ”här”
    ”är”
  10. :gotoloop Vi är nu i steg 4 och slingrar tills en rad innehåller ”bar”
  11. /bar/ loop är klar, ”bar” har hittats, det ” s mönsterutrymmet
  12. mönsterutrymme ersätts med hållutrymme som innehåller alla raderna mellan” foo ”och” bar ”som har sparats under huvudslingan
  13. p kopiera mönsterutrymme till standardutdata

Klar!

Kommentarer

  • Bra gjort, +1. Jag undviker vanligtvis att använda dessa kommandon genom att tr ’ ingöra de nya raderna i SOH och utföra normala sed-kommandon och sedan ersätta de nya raderna.

Lämna ett svar

Din e-postadress kommer inte publiceras. Obligatoriska fält är märkta *