Det ser ud til, at jeg misbruger grep / egrep.

Jeg forsøgte at søge efter strenge i flere linjer og kunne ikke finde et match, mens jeg ved, at det, jeg leder efter, skulle matche. Oprindeligt troede jeg, at mine regexes var forkert, men til sidst læste jeg, at disse værktøjer fungerer pr. linje (også mine regexer var så trivielle, at det ikke kunne være problemet).

Så hvilket værktøj vil man bruge til at søge mønstre på tværs af flere linjer?

Kommentarer

  • mulig duplikat af Multiline mønster match ved hjælp af sed, awk eller grep
  • @CiroSantilli – Jeg tror ikke, at denne Q og den, du linkede til, er duplikater. Den anden Q spørger, hvordan du ‘ d matcher flere linjemønstre (dvs. hvilket værktøj skal / kan jeg bruges til at gøre dette) mens denne spørger, hvordan man gør dette med grep. De er tæt beslægtede, men ikke dups, IMO.
  • @sim disse sager er svært at beslutte: Jeg kan se din pointe. Jeg tror, at denne særlige sag er bedre som en duplikat, fordi se brugeren sagde "grep" foreslår verbet ” til grep “, og top svar, inklusive accepteret, brug ikke ‘ t brug grep.
  • Der er ingen indikation, der viser, at der er brug for et regulært udtryk med flere linjer her. Overvej at vise et faktisk eksempel med inputdata og forventede outputdata samt din tidligere indsats.

Svar

Her “sa sed en, der giver dig grep -lignende adfærd på tværs af flere linjer:

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

Sådan fungerer det

  • -n undertrykker standardopførelsen ved udskrivning af hver linje
  • /foo/{} instruerer den til at matche foo og gør hvad der kommer inden i squigglies til de matchende linjer. Udskift foo med startdelen af mønsteret.
  • :start er en forgreningsetiket, der hjælper os med at holde løkke, indtil vi finder slutningen på vores regex.
  • /bar/!{} udfører, hvad der er i squigglies til de linjer, der ikke matcher bar. Udskift med slutningen af mønsteret.
  • N føjer den næste linje til den aktive buffer (sed kalder dette mønsterområdet)
  • b start vil ubetinget forgrene sig til start -mærket tidligere for at fortsætte med at tilføje den næste linje, så længe mønsterområdet ikke indeholder bar.
  • /your_regex/p udskriver mønsterområdet, hvis det matcher your_regex. Du skal erstatte your_regex med hele det udtryk, du vil matche på tværs af flere linjer.

Kommentarer

  • +1 Tilføjelse af dette til toolikt! Tak.
  • Bemærk: På MacOS giver dette sed: 1: "/foo/{:start /bar/!{N;b ...": unexpected EOF (pending }'s)
  • At få sed: unterminated { fejl
  • @Nomaed Skudt i mørke her, men indeholder din regex tilfældigvis ” {” tegn? Hvis det er tilfældet, skal du ‘ nødt til at backslash-undslippe dem.
  • @Nomaed Det ser ud til at det har at gøre med forskellene mellem sed implementeringer. Jeg forsøgte at følge anbefalingerne i det svar for at gøre ovenstående script standardkompatibelt, men det fortalte mig, at ” start ” var en udefineret etiket. Så jeg ‘ er ikke sikker på, om dette kan gøres på en standardkompatibel måde. Hvis du administrerer det, er du velkommen til at redigere mit svar.

Svar

Jeg bruger generelt et værktøj kaldes pcregrep, som kan installeres i det meste af linux-smag ved hjælp af yum eller apt.

For eksempel.

Antag, at hvis du har en fil med navnet testfile med indhold

abc blah blah blah def blah blah blah 

Du kan køre følgende kommando:

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

for at lave mønstermatchning på tværs af flere linjer.

Desuden du kan også gøre det med sed.

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

Kommentarer

  • dette sed forslag springes over linjen, hvor def findes

Svar

Simpelthen en normal grep, der understøtter Perl-regexp parameter P vil udføre dette job.

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

(?s) kaldes DOTALL-modifikator, der gør prikken i din regex til at matche ikke kun tegnene, men også linjeskiftene.

Kommentarer

  • Når jeg prøver denne løsning, slutter output ikke ved ‘ def ‘ men går til slutningen af filen ‘ blah ‘
  • måske din grep understøtter ikke -P mulighed
  • Dette var det eneste, der fungerede for mig – prøvede alle sed forslag, men gik ‘ ikke så langt som at installere grep-alternativer.
  • $ grep --version: grep (GNU grep) 3.1 i Windows Git Bash har en indstilling -P, --perl-regexp men (?s) betyder ikke ‘ virker ikke der. Det viser stadig kun den første linje. Det samme mønster med den samme teststreng fungerer på regex101.com . Er der et alternativ i Git Bash? sed? (sed (GNU sed) 4.8 her)
  • Ved du hvordan du tilføjer kontekst til output? grep -1 fungerer ikke ‘ her.

Svar

Her er “en enklere tilgang med Perl:

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

eller (da JosephR tog sed rute , jeg stjæler skamløst hans forslag )

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

### Forklaring

$f=join("",<>);: dette læser hele filen og gemmer dens indhold (nye linjer og alt) i variablen $f. Vi forsøger derefter at matche foo\nbar.*\n og udskrive det, hvis det matcher (den specielle variabel $& holder den sidst fundne match). ///m er nødvendig for at få det regulære udtryk til at matche på tværs af nye linjer.

-0 indstiller input-record-separator. Hvis du indstiller dette til 00, aktiveres “afsnitstilstand”, hvor Perl vil bruge fortløbende nye linjer (\n\n) som pladeseparator. I tilfælde, hvor der ikke er nogen på hinanden følgende nye linjer, læses hele filen (slurpes) på én gang.

### Advarsel: Gør ikke dette for store filer, den indlæses hele filen i hukommelsen, og det kan være et problem.

Kommentarer

  • Jeg don ‘ t ved meget om Perl, men ville det ‘ t være nødvendigt at være my $f=join("",<>); strengt taget?
  • kun @Sapphire_Brick hvis du er i streng tilstand (use strict;). Det ‘ er en god vane at komme ind på, især når du skriver større scripts, men det ‘ s overoverførsel til en lille en-liner som denne en.

Svar

Antag, at vi har filen test.txt indeholdende:

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

Følgende kode kan bruges:

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

For følgende output:

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

Svar

Grep-alternativet sift understøtter multiline matching (ansvarsfraskrivelse: Jeg er forfatteren).

Antag testfile indeholder:

 <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>' beskrivelsen)

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 (uddrag og formater beskrivelsen)

Resultat:

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

Kommentarer

  • Meget flot værktøj. Tillykke! Forsøg at medtage det i distributioner som Ubuntu.

Svar

Jeg løste denne for mig ved hjælp af grep og – En indstilling med en anden grep.

grep first_line_word -A 1 testfile | grep second_line_word 

Indstillingen -A 1 udskriver 1 linje efter den fundne linje. Naturligvis afhænger det af din fil og ordkombination. Men for mig var det den hurtigste og mest pålidelige løsning.

Kommentarer

  • alias grepp = ‘ grep –farve = auto -B10 -A20 -i ‘ derefter kat somefile | grepp bla | grepp foo | grepp bar … ja de -A og -B er meget praktiske …du har det bedste svar
  • Dette er ikke ‘ t super deterministisk, og det ignorerer hele mønsteret til fordel for bare at få en anden enkelt linje (bare baseret på dens nærhed til første linje). ‘ er bedre at fortælle programmet at gå, hvor langt det skal gå for at komme til en slags mønster, du ‘ helt sikker er slutningen af den tekst, du ‘ prøver at matche. For eksempel, hvis testfile opdateres således, at second_line_word er på tredje linje, så mangler du ikke kun nu den første linje (pga. dit andet grep) men du ‘ mangler ikke den linje, der begyndte at vises mellem de to.
  • Dette ville være en god nok MO til ad hoc-kommandoer, hvor du virkelig bare vil have en enkelt linje i output, som du allerede har forstået. Jeg tror ikke ‘ at ‘ er hvad OP er efter, og du kunne sandsynligvis også bare kopiere / indsætte på det tidspunkt pga. det er ad hoc.

Svar

En måde at gøre dette på er med Perl. for eksempel. her “er indholdet af en fil med navnet foo:

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

Her er her nogle Perl, som vil match mod enhver linje, der begynder med foo efterfulgt af enhver linje, der begynder 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, opdelt:

  • while(<>){$all .= $_} Dette indlæser hele standardindgangen til variablen $all
  • while($all =~ Mens variablen all har det regulære udtryk …
  • /^(foo[^\n]*\nbar[^\n]*\n)/m Regex: foo i begyndelsen af linjen efterfulgt af et vilkårligt antal ikke-nylinjetegn, efterfulgt af en ny linje, efterfulgt straks af “bjælke” og resten af linjen med bjælke i den. /m i slutningen af regex betyder “match på tværs af flere linjer”
  • print $1 Udskriv den del af regex der var i parentes (i dette tilfælde hele det regulære udtryk)
  • s/^(foo[^\n]*\nbar[^\n]*\n)//m Slet det første match for regex, så vi kan matche flere tilfælde af regex i den pågældende fil

Og output:

foo line 1 bar line 2 foo bar line 6 

Kommentarer

  • Bare kom forbi for at sige, at din Perl kan afkortes til det mere idiomatiske: perl -n0777E 'say $& while /^foo.*\nbar.*\n/mg' foo

Svar

Hvis vi ønsker at få teksten mellem de to mønstre eksklusive sig selv.

Antag at vi har filen test.txt indeholdende:

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

Følgende kode kan bruges:

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

For følgende output:

here is the text to keep between the 2 patterns 

Hvordan fungerer det, lad “s gør det trin for trin

  1. /foo/{ udløses, når linjen indeholder “foo”
  2. n udskift mønsterområdet med næste linje, dvs. ordet “her”
  3. b gotoloop gren til etiketten “gotoloop”
  4. :gotoloop definerer etiketten “gotoloop”
  5. /bar/!{ hvis mønsteret ikke indeholder “bar”
  6. h udskift holdrummet med mønster, så “her” gemmes i holdrummet
  7. b loop gren til etiketten “loop”
  8. :loop definerer etiketten “loop”
  9. N tilføjer mønsteret til holdrummet.
    Holdrummet indeholder nu:
    “her”
    “er”
  10. :gotoloop Vi er nu i trin 4 og sløjfer, indtil en linje indeholder “bar”
  11. /bar/ loop er færdig, “bar” er fundet, det ” s mønsterområdet
  12. mønsterplads erstattes med holdrum, der indeholder alle linjerne mellem” foo “og” bar “, der er gemt under hovedsløjfen
  13. p kopier mønsterplads til standardoutput

Udført!

Kommentarer

  • Godt gået, +1. Jeg undgår normalt at bruge disse kommandoer ved at tr ‘ indføre de nye linjer i SOH og udføre normale sed-kommandoer og derefter erstatte de nye linjer.

Skriv et svar

Din e-mailadresse vil ikke blive publiceret. Krævede felter er markeret med *