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
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ă cufoo
și faceți ceea ce vine în interiorul squigglies la liniile potrivite. Înlocuițifoo
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 cubar
. Î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 etichetastart
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ținebar
. -
/your_regex/p
imprimă spațiul modelului dacă se potrivește cuyour_regex
. Ar trebui să înlocuițiyour_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ăsidef
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ătestfile
este actualizat astfel încâtsecond_line_word
să 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ă.
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 variabilaall
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
-
/foo/{
este declanșat atunci când linia conține „foo” -
n
înlocuiți spațiul modelului cu următoarea linie, adică cuvântul „aici” -
b gotoloop
ramură la eticheta „gotoloop” -
:gotoloop
definește eticheta „gotoloop” -
/bar/!{
dacă modelul nu conține „bara” -
h
înlocuiți spațiul de așteptare cu model, așa că „aici” este salvat în spațiul de așteptare -
b loop
ramificație către eticheta „buclă” -
:loop
definește eticheta „buclă” -
N
adaugă modelul la spațiul de așteptare.
Acum, spațiul de așteptare conține:
„aici”
„este„ -
:gotoloop
Suntem acum la pasul 4 și buclăm până când o linie conține „bară” -
/bar/
buclă este terminată, „bar” a fost găsit, acesta ” s spațiul modelului - 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
-
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.
grep
. Sunt strâns legate, dar nu înșelăciuni, IMO."grep"
sugerând verbul ” pentru grep ” și răspunsuri de top, inclusiv acceptat, nu ‘ t utiliza grep.