Se pare că folosesc greșit grep / egrep.

Încercam să caut șiruri în mai multe linii și nu am putut găsi o potrivire în timp ce știu că ceea ce caut ar trebui să se potrivească. Inițial am crezut că regexurile mele erau greșite, dar în cele din urmă am citit că acestea instrumentele funcționează pe linie (de asemenea, regexele mele au fost atât de banale încât nu ar putea fi problema).

Deci, ce instrument ar folosi unul pentru a căuta modele pe mai multe linii?

Comentarii

  • posibil duplicat al potrivire model multiliniu utilizând sed, awk sau grep
  • @CiroSantilli – Nu cred că acest Q și cel pe care l-ați conectat sunt duplicate. Celălalt Q vă întreabă cum ‘ faceți potrivirea modelului cu mai multe linii (adică ce instrument ar trebui / pot să folosiți pentru a face acest lucru) în timp ce acesta întreabă cum să faceți acest lucru cu grep. Sunt strâns legate, dar nu înșelăciuni, IMO.
  • @sim aceste cazuri sunt greu de decis: îți văd ideea. Cred că acest caz special este mai bun ca duplicat utilizatorul a spus "grep" sugerând verbul ” pentru grep ” și răspunsuri de top, inclusiv acceptat, nu ‘ t utiliza grep.
  • Nu există nicio indicație care să arate că este necesară o expresie regulată pe mai multe linii aici. Vă rugăm să luați în considerare afișarea unui exemplu real cu date de intrare și date de ieșire preconizate, precum și efortul dvs. anterior.

Răspuns

Iată „un sed unul care vă va oferi un comportament similar cu grep pe mai multe linii:

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

Cum funcționează

  • -n suprimă comportamentul implicit al imprimării fiecărei linii
  • /foo/{} îl instruiește să se potrivească cu foo și faceți ceea ce vine în interiorul squigglies la liniile potrivite. Înlocuiți foo cu partea de pornire a modelului.
  • :start este o etichetă de ramificare care ne ajută să continuăm să ne buclăm până găsim sfârșitul regexului nostru.
  • /bar/!{} va executa ceea ce este în squigglies pentru liniile care nu se potrivesc cu bar. Înlocuiți cu partea finală a modelului.
  • N adaugă următoarea linie la bufferul activ (sed numește acest spațiu model)
  • b start se va ramifica necondiționat către eticheta start pe care am creat-o mai devreme pentru a continua să adăugați următoarea linie, atâta timp cât spațiul modelului nu conține bar.
  • /your_regex/p imprimă spațiul modelului dacă se potrivește cu your_regex. Ar trebui să înlocuiți your_regex cu întreaga expresie pe care doriți să o potriviți pe mai multe linii.

Comentarii

  • +1 Adăugarea acestuia în toolikt! Vă mulțumim.
  • Notă: pe MacOS, aceasta dă sed: 1: "/foo/{:start /bar/!{N;b ...": unexpected EOF (pending }'s)
  • Obținerea sed: unterminated { eroare
  • @Nomaed Shot în întuneric aici, dar regexul tău conține orice ” {” caractere? Dacă da, ‘ va trebui să le înlăturați, să le scăpați.
  • @Nomaed Se pare că are de-a face cu diferențele dintre sed implementări. Am încercat să urmez recomandările din acest răspuns pentru a face ca scriptul de mai sus să fie compatibil cu standardul, dar mi-a spus că ” start ” a fost un nedefinit eticheta. Deci, nu ‘ nu sunt sigur dacă acest lucru se poate face într-un mod conform standardului. Dacă îl gestionați, vă rugăm să nu ezitați să modificați răspunsul meu.

Răspuns

În general, folosesc un instrument numit pcregrep care poate fi instalat în cea mai mare parte a linux-ului folosind yum sau apt.

De exemplu.

Să presupunem dacă aveți un fișier numit testfile cu conținut

abc blah blah blah def blah blah blah 

Puteți rula următoarea comandă:

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

pentru a face potrivirea modelului pe mai multe linii.

Mai mult, puteți face același lucru și cu sed.

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

Comentarii

  • această sed omite linia în care s-ar găsi def

Răspuns

Pur și simplu un grep normal care acceptă parametrul Perl-regexp P va face această treabă.

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

(?s) numit modificator DOTALL care face ca punctul din regex să se potrivească nu numai cu caracterele, ci și cu întreruperile de linie.

Comentarii

  • Când încerc această soluție, ieșirea nu se termină la ‘ def ‘ dar merge la sfârșitul fișierului ‘ bla ‘
  • poate grep-ul dvs. nu acceptă opțiunea -P
  • Aceasta a fost singura care a funcționat pentru mine – am încercat toate sed sugestii, dar ‘ nu a mers până la instalarea alternativelor grep.
  • $ grep --version: grep (GNU grep) 3.1 în Windows Git Bash are o opțiune -P, --perl-regexp, dar (?s) nu ‘ pare să funcționeze acolo. În continuare arată doar prima linie. Același model cu același șir de testare funcționează pe regex101.com . Există o alternativă în Git Bash? sed? (sed (GNU sed) 4.8 aici)
  • Știți cum să adăugați context la ieșire? grep -1 nu funcționează aici.

Răspunde

Aici „o abordare mai simplă folosind Perl:

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

sau (deoarece JosephR a luat ruta , îi voi fura cu nerușinare sugestia )

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

### Explicație

$f=join("",<>);: acesta citește întregul fișier și salvează conținutul acestuia (linii noi și toate) în variabila $f. Încercăm apoi să potrivim foo\nbar.*\n și îl imprimăm dacă se potrivește (variabila specială $& conține ultima potrivire găsită). ///m este necesar pentru ca expresia regulată să se potrivească între linii noi.

-0 setează separatorul de înregistrări de intrare. Setarea acestei opțiuni la 00 activează „modul paragraf”, unde Perl va folosi linii noi consecutive (\n\n) ca separator de înregistrări. În cazurile în care nu există linii noi consecutive, întregul fișier este citit (glisat) simultan.

### Atenție: nu faceți acest lucru pentru fișierele mari, acesta va fi încărcat întregul fișier în memorie și poate fi o problemă.

Comentarii

  • Nu ‘ t știi multe despre Perl, dar nu ar trebui ‘ să fie my $f=join("",<>);, strict vorbind?
  • @Sapphire_Brick only dacă vă aflați în modul strict (use strict;). Este ‘ un obicei bun pentru a intra, mai ales atunci când scriem scripturi mai mari, dar este ‘ depășire pentru un mic liner ca acesta unul.

Răspuns

Să presupunem că avem fișierul test.txt conținând:

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

Se poate utiliza următorul cod:

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

Pentru următoarea ieșire:

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

Răspuns

Alternativa grep sift acceptă potrivirea pe mai multe linii (responsabilitate: eu sunt autorul).

Să presupunem că testfile conține:

 <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>' (arată liniile care conțin descrierea)

Rezultat:

 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 (extrageți și reformatați descrierea)

Rezultat:

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

Comentarii

  • Instrument foarte frumos. Felicitări! Încercați să-l includeți în distribuții precum Ubuntu.

Răspuns

Am rezolvat acest lucru pentru mine folosind grep și – O opțiune cu un alt grep.

grep first_line_word -A 1 testfile | grep second_line_word 

Opțiunea -A 1 imprimă 1 linie după linia găsită. Desigur, depinde de combinația de fișiere și cuvinte. Dar pentru mine a fost cea mai rapidă și fiabilă soluție.

Comentarii

  • alias grepp = ‘ grep –color = auto -B10 -A20 -i ‘ apoi cat somefile | grepp bla | grepp foo | grepp bar … da cei -A și -B sunt foarte la îndemână …aveți cel mai bun răspuns
  • Acest lucru nu este

t super determinist și ignoră întregul model în favoarea obținerii unei singure linii diferite (doar pe baza proximității sale la prima linie). ‘ este mai bine să spuneți programului să meargă oricât de departe trebuie să meargă pentru a ajunge la un fel de tipar pe care ‘ îl re absolut sigur este sfârșitul textului pe care ‘ încercați să îl potriviți. De exemplu, dacătestfileeste actualizat astfel încâtsecond_line_wordsă fie pe a treia linie, atunci nu numai că vă lipsește acum prima linie (din cauza al doileagrep), dar ‘ nu lipsește linia care a început să apară între cele două.

  • Acest lucru ar fi un MO suficient de bun pentru comenzi ad hoc în care chiar doriți doar o singură linie în ieșire pe care deja ați înțeles-o. Nu ‘ nu cred că ‘ este totuși ce urmărește OP-ul și probabil că ai putea copia și lipi în acel moment din cauza fiind ad hoc.
  • Răspuns

    O modalitate de a face acest lucru este cu Perl. de exemplu. aici este conținutul unui fișier numit foo:

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

    Acum, aici sunt câteva Perl care vor meci împotriva oricărei linii care începe cu foo urmată de orice linie care începe cu 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, defalcat:

    • while(<>){$all .= $_} Aceasta încarcă întreaga intrare standard în variabila $all
    • while($all =~ În timp ce variabila all are expresia regulată …
    • /^(foo[^\n]*\nbar[^\n]*\n)/m Regex: foo la începutul liniei, urmat de orice număr de caractere non-newline, urmat de o linie nouă, urmată imediat de „bara”, iar restul liniei cu bara în ea. /m la sfârșitul regexului înseamnă „potrivire pe mai multe linii”
    • print $1 Imprimați partea regexului care era în paranteză (în acest caz, întreaga expresie regulată)
    • s/^(foo[^\n]*\nbar[^\n]*\n)//m Ștergeți prima potrivire pentru regex, astfel încât să putem potrivi mai multe cazuri de regex în fișierul în cauză

    Și rezultatul:

    foo line 1 bar line 2 foo bar line 6 

    Comentarii

    • Tocmai a ieșit pentru a spune că Perl poate fi scurtat la cel mai idiomatic: perl -n0777E 'say $& while /^foo.*\nbar.*\n/mg' foo

    Răspuns

    Dacă vrem să obținem textul între cele două modele, excluzându-se singuri.

    Să presupunem că avem fișierul test.txt conținând:

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

    Se poate utiliza următorul cod:

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

    Pentru următoarea ieșire:

    here is the text to keep between the 2 patterns 

    Cum funcționează, să faceți-l pas cu pas

    1. /foo/{ este declanșat atunci când linia conține „foo”
    2. n înlocuiți spațiul modelului cu următoarea linie, adică cuvântul „aici”
    3. b gotoloop ramură la eticheta „gotoloop”
    4. :gotoloop definește eticheta „gotoloop”
    5. /bar/!{ dacă modelul nu conține „bara”
    6. h înlocuiți spațiul de așteptare cu model, așa că „aici” este salvat în spațiul de așteptare
    7. b loop ramificație către eticheta „buclă”
    8. :loop definește eticheta „buclă”
    9. N adaugă modelul la spațiul de așteptare.
      Acum, spațiul de așteptare conține:
      „aici”
      „este„
    10. :gotoloop Suntem acum la pasul 4 și buclăm până când o linie conține „bară”
    11. /bar/ buclă este terminată, „bar” a fost găsit, acesta ” s spațiul modelului
    12. spațiul modelului este înlocuit cu spațiul de așteptare care conține toate liniile dintre„ foo ”și„ bară ”care au fost salvate în timpul buclei principale
    13. p copiați spațiul modelului la ieșirea standard

    Gata!

    Comentarii

    • Bravo, +1. De obicei, evit să folosesc aceste comenzi tr ‘ introducând noile linii în SOH și executând comenzi sed normale, apoi înlocuind noile linii.

    Lasă un răspuns

    Adresa ta de email nu va fi publicată. Câmpurile obligatorii sunt marcate cu *