Sembra che io stia utilizzando in modo improprio grep / egrep.

Stavo cercando di cercare stringhe su più righe e non sono riuscito a trovare una corrispondenza mentre so che ciò che sto cercando dovrebbe corrispondere. Allinizio pensavo che le mie regex fossero sbagliate ma alla fine ho letto che queste gli strumenti funzionano per riga (anche le mie espressioni regolari erano così banali che non poteva essere il problema).

Quindi quale strumento si usa per cercare pattern su più righe?

Commenti

  • possibile duplicato di Corrispondenza pattern multilinea utilizzando sed, awk o grep
  • @CiroSantilli – Non penso che questa Q e quella a cui hai collegato siano duplicati. Laltra Q sta chiedendo come ‘ faresti la corrispondenza di pattern su più righe (cioè quale strumento devo / posso utilizzare per farlo) mentre questo chiede come farlo con grep. Sono strettamente correlati ma non doppi, IMO.
  • @sim quei casi sono difficile da decidere: posso vedere il tuo punto, penso che questo caso particolare sia migliore perché duplicato perché se lutente ha detto "grep" suggerendo il verbo ” a grep ” e le risposte migliori, includendo accettato, don ‘ t usa grep.
  • Non ci sono indicazioni per mostrare che qui sia necessaria unespressione regolare su più righe. Considera la possibilità di mostrare un esempio effettivo con i dati di input e i dati di output previsti, nonché il tuo lavoro precedente.

Risposta

Qui “sa sed uno che ti darà un comportamento simile a grep su più righe:

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

Come funziona

  • -n sopprime il comportamento predefinito di stampa di ogni riga
  • /foo/{} indica di corrispondere a foo e fai ciò che si trova allinterno degli squiggli alle linee corrispondenti. Sostituisci foo con la parte iniziale del pattern.
  • :start è unetichetta di ramificazione che ci aiuta a continuare il ciclo finché non troviamo la fine della nostra regex.
  • /bar/!{} eseguirà ciò che “s negli squigglies per le righe che non corrispondono a bar. Sostituisci con la parte finale del pattern.
  • N aggiunge la riga successiva al buffer attivo (sed lo chiama lo spazio del modello)
  • b start si diramerà incondizionatamente alletichetta start che abbiamo creato in precedenza in modo da continuare ad aggiungere la riga successiva fintanto che lo spazio del pattern non “t contiene bar.
  • /your_regex/p stampa lo spazio del pattern se corrisponde a your_regex. È necessario sostituire your_regex con lintera espressione che si desidera trovare su più righe.

Commenti

  • +1 Aggiungendo questo al toolikt! Grazie.
  • Nota. Su MacOS questo dà sed: 1: "/foo/{:start /bar/!{N;b ...": unexpected EOF (pending }'s)
  • Ricezione di sed: unterminated { errore
  • @Nomaed Shot in the dark here, ma la tua espressione regolare contiene caratteri ” {“? In tal caso, ‘ dovrai eseguire lescape con la barra rovesciata.
  • @Nomaed Sembra che abbia a che fare con le differenze tra le sed implementazioni. Ho provato a seguire i consigli in quella risposta per rendere lo script precedente conforme allo standard, ma mi è stato detto che ” start ” era undefined etichetta. Quindi ‘ non sono sicuro che ciò possa essere fatto in modo conforme agli standard. Se riesci a gestirlo, non esitare a modificare la mia risposta.

Risposta

In genere utilizzo uno strumento chiamato pcregrep che può essere installato nella maggior parte delle versioni di Linux utilizzando yum o apt.

Ad esempio.

Supponi di avere un file denominato testfile con contenuto

abc blah blah blah def blah blah blah 

Puoi eseguire il seguente comando:

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

per eseguire la corrispondenza di modelli su più righe.

Inoltre, puoi fare lo stesso anche con sed.

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

Commenti

  • questo sed suggerimento salta la riga in cui si trova def

Risposta

Semplicemente un normale grep che supporta il Perl-regexp parametro P farà questo lavoro.

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

(?s) chiamato modificatore DOTALL che fa sì che il punto nella tua espressione regolare corrisponda non solo ai caratteri ma anche alle interruzioni di riga.

Commenti

  • Quando provo questa soluzione loutput non termina con ‘ def ‘ ma va alla fine del file ‘ blah ‘
  • forse il tuo grep non supporta lopzione -P
  • Questa era lunica che ha funzionato per me – ho provato tutti i sed suggerimenti, ma ‘ non è arrivato fino allinstallazione di alternative grep.
  • $ grep --version: grep (GNU grep) 3.1 in Windows Git Bash ha unopzione -P, --perl-regexp ma (?s) non ‘ sembra che non funzioni lì. Mostra ancora solo la prima riga. Lo stesso pattern con la stessa stringa di test funziona su regex101.com . Esiste unalternativa in Git Bash? sed? (sed (GNU sed) 4.8 qui)
  • Sai come aggiungere il contesto alloutput? grep -1 non ‘ funziona qui.

Risposta

Ecco “un approccio più semplice utilizzando Perl:

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

o (poiché JosephR ha preso il sed route , ruberò spudoratamente il suo suggerimento )

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

### Spiegazione

$f=join("",<>);: questo legge lintero file e salva il suo contenuto (nuove righe e tutto) nella variabile $f. Tentiamo quindi di trovare una corrispondenza con foo\nbar.*\n e di stamparla se corrisponde (la variabile speciale $& contiene lultima corrispondenza trovata). ///m è necessario per fare in modo che lespressione regolare corrisponda tra le nuove righe.

-0 imposta il separatore del record di input. Impostando questo a 00 si attiva la “modalità paragrafo” dove Perl userà le nuove righe consecutive (\n\n) come separatore di record. Nei casi in cui non ci sono nuove righe consecutive, lintero file viene letto (slurpato) in una volta.

### Attenzione: non farlo per file di grandi dimensioni, verrà caricato lintero file in memoria e questo potrebbe essere un problema.

Commenti

  • Non ‘ t so molto di Perl, ma ‘ t deve essere my $f=join("",<>);, in senso stretto?
  • solo @Sapphire_Brick se sei in modalità rigorosa (use strict;). ‘ è una buona abitudine da prendere, soprattutto quando si scrivono script più grandi, ma ‘ è troppo faticoso per una battuta come questa uno.

Risposta

Supponiamo di avere il file test.txt contenente:

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

È possibile utilizzare il seguente codice:

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

Per il seguente output:

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

Risposta

Lalternativa grep sift supporta la corrispondenza multilinea (disclaimer: io sono lautore).

Supponi testfile contiene:

 <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>' (mostra le righe che contengono la descrizione)

Risultato:

 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 (estrae e riformattare la descrizione)

Risultato:

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

Commenti

  • Strumento molto carino. Congratulazioni! Prova a includerlo in distribuzioni come Ubuntu.

Risposta

Ho risolto questo per me usando grep e – Unopzione con un altro grep.

grep first_line_word -A 1 testfile | grep second_line_word 

Lopzione -A 1 stampa 1 riga dopo la riga trovata. Ovviamente dipende dal file e dalla combinazione di parole. Ma per me era la soluzione più veloce e affidabile.

Commenti

  • alias grepp = ‘ grep –color = auto -B10 -A20 -i ‘ quindi cat somefile | grepp blah | grepp foo | grepp bar … sì, quelli -A e -B sono molto utili …hai la risposta migliore
  • Questo non è ‘ t super deterministico e ignora lintero schema a favore di ottenere solo una singola riga diversa (basata solo sulla sua prossimità alla prima riga). ‘ è meglio dire al programma di spingersi oltre il limite necessario per arrivare a una sorta di schema che ‘ re assolutamente certo è la fine del testo che ‘ stai cercando di abbinare. Ad esempio, se testfile viene aggiornato in modo tale che second_line_word si trovi sulla terza riga, non solo ti manca la prima riga (a causa di il secondo grep) ma ‘ non manca la riga che ha iniziato a comparire tra i due.
  • Questo sarebbe un MO abbastanza buono per comandi ad hoc in cui vuoi davvero solo una singola riga in output che hai già capito. Non ‘ penso che ‘ sia ciò che sta cercando lOP e probabilmente potresti anche copiare / incollare a quel punto a causa di essendo ad hoc.

Risposta

Un modo per farlo è con Perl. per esempio. ecco il contenuto di un file denominato foo:

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

Ora, ecco un po di Perl che corrisponde a qualsiasi riga che inizia con foo seguita da qualsiasi riga che inizia con 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; }" 

Il Perl, suddiviso:

  • while(<>){$all .= $_} Questo carica lintero standard input nella variabile $all
  • while($all =~ Mentre la variabile all ha lespressione regolare …
  • /^(foo[^\n]*\nbar[^\n]*\n)/m Lespressione regolare: foo allinizio della riga, seguito da un numero qualsiasi di caratteri non di nuova riga, seguito da una nuova riga, seguita immediatamente da “bar” e dal resto della riga contenente una barra. /m alla fine della regex significa “corrispondenza su più righe”
  • print $1 Stampa la parte della regex che era tra parentesi (in questo caso, lintera espressione regolare)
  • s/^(foo[^\n]*\nbar[^\n]*\n)//m Cancella la prima corrispondenza per la regex, così possiamo trovare più casi della regex nel file in questione

E loutput:

foo line 1 bar line 2 foo bar line 6 

Commenti

  • Sono appena passato per dire che il tuo Perl può essere abbreviato nel modo più idiomatico: perl -n0777E 'say $& while /^foo.*\nbar.*\n/mg' foo

Risposta

Se vogliamo che il testo tra i 2 pattern si escluda.

Supponiamo di avere il file test.txt contenente:

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

È possibile utilizzare il codice seguente:

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

Per il seguente output:

here is the text to keep between the 2 patterns 

Come funziona, let “s passo dopo passo

  1. /foo/{ viene attivato quando la riga contiene “foo”
  2. n sostituisci lo spazio del pattern con la riga successiva, cioè la parola “qui”
  3. b gotoloop ramo con letichetta “gotoloop”
  4. :gotoloop definisce letichetta “gotoloop”
  5. /bar/!{ se il pattern non “t contiene” bar “
  6. h sostituisci lo spazio di conservazione con il motivo, quindi “here” viene salvato nello spazio di conservazione
  7. b loop si dirama alletichetta “loop”
  8. :loop definisce letichetta “loop”
  9. N aggiunge il pattern allo spazio di blocco.
    Ora lo spazio di blocco contiene:
    “here”
    “è”
  10. :gotoloop Siamo ora al passaggio 4 e ripetiamo il ciclo finché una riga non contiene “bar”
  11. /bar/ il ciclo è terminato, “bar” è stato trovato, it ” è lo spazio del pattern
  12. lo spazio del pattern viene sostituito con lo spazio di attesa che contiene tutte le linee tra” foo “e” bar “che sono state salvate durante il ciclo principale
  13. p copia lo spazio del modello nelloutput standard

Fatto!

Commenti

  • Ben fatto, +1. Di solito evito di usare questi comandi tr ‘ inserendo le nuove righe in SOH ed eseguendo normali comandi sed, quindi sostituendo le nuove righe.

Lascia un commento

Il tuo indirizzo email non sarà pubblicato. I campi obbligatori sono contrassegnati *