Come usare sed / grep per estrarre il testo tra due parole?


134

Sto provando a generare una stringa che contiene tutto tra due parole di una stringa:

ingresso:

"Here is a String"

produzione:

"is a"

usando:

sed -n '/Here/,/String/p'

include gli endpoint, ma non voglio includerli.


8
Quale dovrebbe essere il risultato se l'ingresso è Here is a Here String? Oppure I Hereby Dub Thee Sir Stringy?
ghoti,

5
FYI. Il tuo comando significa stampare tutto tra la riga che ha la parola Here e la riga che ha la parola String - non quello che vuoi.
Hai Vu,

L'altra sedFAQ comune è "come posso estrarre il testo tra righe particolari"; questo è stackoverflow.com/questions/16643288/...
tripleee

Risposte:


109
sed -e 's/Here\(.*\)String/\1/'

2
Grazie! E se volessi trovare tutto tra "one is" e "String" in "Here is a one is a String"? (sed -e 's / one is (. *) String / \ 1 /'?
user1190650

5
@ user1190650 Funzionerebbe se vuoi vedere anche "Here is a". È possibile provarlo: echo "Here is a one is a String" | sed -e 's/one is\(.*\)String/\1/'. Se si desidera solo la parte tra "è" e "String", allora avete bisogno di rendere l'espressione regolare corrisponde tutta la linea: sed -e 's/.*one is\(.*\)String.*/\1/'. In sed, s/pattern/replacement/dire "sostituisci" sostituzione "con" modello "su ogni riga". Cambierà solo tutto ciò che corrisponde a "pattern", quindi se vuoi che sostituisca l'intera linea, devi far corrispondere "pattern" all'intera linea.
Brian Campbell,

9
Questo si interrompe quando l'ingresso èHere is a String Here is a String
Jay D,

1
Sarebbe bello vedere la soluzione per un caso: "Here is a blah blah String Here is 1 a blah blah String Here is 2 a blash blash String" L'output dovrebbe raccogliere solo la prima sottostringa tra Here e String "
Jay D

1
@JayD sed non supporta la corrispondenza non avida, vedi questa domanda per alcune alternative consigliate.
Brian Campbell,

180

GNU grep può anche supportare look-ahead e look-back positivi e negativi: nel tuo caso, il comando sarebbe:

echo "Here is a string" | grep -o -P '(?<=Here).*(?=string)'

Se sono presenti più occorrenze di Heree string, è possibile scegliere se si desidera abbinare la prima Heree l'ultima stringoppure abbinarle singolarmente. In termini di regex, si chiama match avido (primo caso) o match non avido (secondo caso)

$ echo 'Here is a string, and Here is another string.' | grep -oP '(?<=Here).*(?=string)' # Greedy match
 is a string, and Here is another 
$ echo 'Here is a string, and Here is another string.' | grep -oP '(?<=Here).*?(?=string)' # Non-greedy match (Notice the '?' after '*' in .*)
 is a 
 is another 

31
Nota che l' -Popzione GNU grep non esiste nel grepincluso in * BSD o in quelli forniti con SVR4 (Solaris, ecc.). In FreeBSD, puoi installare la devel/pcreporta che include pcregrep, che supporta PCRE (e guardare avanti / indietro). Le versioni precedenti di OSX utilizzavano GNU grep, ma in OSX Mavericks -Pderiva dalla versione di FreeBSD, che non include l'opzione.
ghoti,

1
Ciao, come posso estrarre solo contenuti distinti?
Durgesh Suthar,

4
Questo non funziona perché se la stringa "stringa" finale si verifica più di una volta, otterrà l' ultima occorrenza, non la ricorrenza successiva .
Buttle Butkus,

6
In caso di Here is a string a string, entrambi " is a " e " is a string a "sono risposte valide (ignorare le virgolette), secondo i requisiti della domanda. Dipende da voi quale di questi si desidera e poi risposta può essere diversa di conseguenza. Ad ogni modo, per le tue esigenze, questo funzionerà:echo "Here is a string a string" | grep -o -P '(?<=Here).*?(?=string)'
anishsane,

2
@BND, devi abilitare la funzione di ricerca multilinea di pcregrep . echo $'Here is \na string' | grep -zoP '(?<=Here)(?s).*(?=string)'
anishsane

58

La risposta accettata non rimuove il testo che potrebbe essere precedente Hereo successivo String. Questo sarà:

sed -e 's/.*Here\(.*\)String.*/\1/'

La differenza principale è l'aggiunta di .*immediatamente prima Heree dopo String.


La tua risposta è promettente. Un problema però. Come posso estrarlo nella prima stringa vista se ci sono più stringhe nella stessa riga? Grazie
Mian Asbat Ahmad il

@MianAsbatAhmad Vorresti rendere il *quantificatore, tra Heree String, non avido (o pigro). Tuttavia, il tipo di regex utilizzato da sed non supporta i quantificatori pigri ( ?subito dopo .*) secondo questa domanda StackOverflow. Di solito per implementare un quantificatore pigro si sarebbe solo corrispondere contro tutto, tranne il token non volevi a partita, ma in questo caso, non c'è solo un unico token, invece la sua tutta una serie, String.
Wheeler,

Grazie, ho avuto la risposta utilizzando awk, stackoverflow.com/questions/51041463/...
Mian Asbat Ahmad

Sfortunatamente questo non funziona se la stringa ha interruzioni di riga
Witalo Benicio,

Non dovrebbe. .non corrisponde alle interruzioni di riga. Se si desidera abbinare le interruzioni di riga, è possibile sostituire .con qualcosa di simile [\s\s].
Wheeler,

35

Puoi rimuovere le stringhe da solo in Bash :

$ foo="Here is a String"
$ foo=${foo##*Here }
$ echo "$foo"
is a String
$ foo=${foo%% String*}
$ echo "$foo"
is a
$

E se hai un grep GNU che include PCRE , puoi usare un'asserzione di larghezza zero:

$ echo "Here is a String" | grep -Po '(?<=(Here )).*(?= String)'
is a

perché questo metodo è così lento? quando si toglie una grande pagina html usando questo metodo ci vogliono circa 10 secondi.
Adam Johns,

@AdamJohns, quale metodo? Quello PCRE? PCRE è abbastanza complesso da analizzare, ma 10 secondi sembrano estremi. Se sei preoccupato, ti consiglio di porre una domanda che includa un codice di esempio e di vedere cosa dicono gli esperti.
Ghoti,

Penso che sia stato così lento per me perché conteneva una sorgente di file html molto grande in una variabile. Quando ho scritto i contenuti su file e poi ho analizzato il file, la velocità è aumentata notevolmente.
Adam Johns,

22

Attraverso GNU awk,

$ echo "Here is a string" | awk -v FS="(Here|string)" '{print $2}'
 is a 

grep con -P( perl-regexp ) supporta i parametri \K, che aiuta a scartare i caratteri precedentemente abbinati. Nel nostro caso, la stringa precedentemente abbinata era Herequindi scartata dall'output finale.

$ echo "Here is a string" | grep -oP 'Here\K.*(?=string)'
 is a 
$ echo "Here is a string" | grep -oP 'Here\K(?:(?!string).)*'
 is a 

Se desideri che l'output sia, is apuoi provare quanto segue,

$ echo "Here is a string" | grep -oP 'Here\s*\K.*(?=\s+string)'
is a
$ echo "Here is a string" | grep -oP 'Here\s*\K(?:(?!\s+string).)*'
is a

Questo non funziona per: echo "Here is a string dfdsf Here is a string" | awk -v FS="(Here|string)" '{print $2}'ritorna solo is ainvece di dovrebbe essere is a is a@Avinash Raj
alper

20

Se hai un file lungo con molte occorrenze su più righe, è utile stampare prima le linee numeriche:

cat -n file | sed -n '/Here/,/String/p'

3
Grazie! Questa è l'unica soluzione che ha funzionato nel mio caso (file di testo su più righe, anziché una singola stringa senza interruzioni di riga). Ovviamente, per averlo senza numerazione delle righe, l' -nopzione in catdeve essere omessa.
Jeffrey Lebowski il

... nel qual caso catpuò essere completamente omesso; sedsa leggere un file o input standard.
Tripleee,

9

Questo potrebbe funzionare per te (GNU sed):

sed '/Here/!d;s//&\n/;s/.*\n//;:a;/String/bb;$!{n;ba};:b;s//\n&/;P;D' file 

Ciò presenta ciascuna rappresentazione del testo tra due marcatori (in questo caso Heree String) su una nuova riga e conserva le nuove righe all'interno del testo.


7

Tutte le soluzioni di cui sopra presentano carenze in cui l'ultima stringa di ricerca viene ripetuta altrove nella stringa. Ho trovato il modo migliore per scrivere una funzione bash.

    function str_str {
      local str
      str="${1#*${2}}"
      str="${str%%$3*}"
      echo -n "$str"
    }

    # test it ...
    mystr="this is a string"
    str_str "$mystr" "this " " string"

6

È possibile utilizzare due comandi

$ echo "Here is a String" | sed 's/.*Here//; s/String.*//'
 is a 

Funziona anche

$ echo "Here is a StringHere is a String" | sed 's/.*Here//; s/String.*//'
 is a

$ echo "Here is a StringHere is a StringHere is a StringHere is a String" | sed 's/.*Here//; s/String.*//'
 is a 

6

Capire sed comando, dobbiamo costruirlo passo dopo passo.

Ecco il tuo testo originale

user@linux:~$ echo "Here is a String"
Here is a String
user@linux:~$ 

Proviamo a rimuovere la Herestringa con sl'opzione ubstition insed

user@linux:~$ echo "Here is a String" | sed 's/Here //'
is a String
user@linux:~$ 

A questo punto, credo che saresti in grado di rimuovere Stringanche

user@linux:~$ echo "Here is a String" | sed 's/String//'
Here is a
user@linux:~$ 

Ma questo non è l'output desiderato.

Per combinare due comandi sed, utilizzare l' -eopzione

user@linux:~$ echo "Here is a String" | sed -e 's/Here //' -e 's/String//'
is a
user@linux:~$ 

Spero che questo ti aiuti


4

È possibile utilizzare \1(consultare http://www.grymoire.com/Unix/Sed.html#uh-4 ):

echo "Hello is a String" | sed 's/Hello\(.*\)String/\1/g'

Il contenuto all'interno delle parentesi verrà memorizzato come \1.


Questo rimuove le stringhe invece di produrre qualcosa in mezzo. Prova a rimuovere "Hello" con "is" nel comando sed e verrà visualizzato "Hello a"
Jonathan,

1

Problema. I messaggi di Claws Mail memorizzati sono racchiusi come segue e sto cercando di estrarre le righe dell'oggetto:

Subject: [SLC38A9 lysosomal arginine sensor; mTORC1 pathway] Key molecular
 link in major cell growth pathway: Findings point to new potential
 therapeutic target in pancreatic cancer [mTORC1 Activator SLC38A9 Is
 Required to Efflux Essential Amino Acids from Lysosomes and Use Protein as
 a Nutrient] [Re: Nutrient sensor in key growth-regulating metabolic pathway
 identified [Lysosomal amino acid transporter SLC38A9 signals arginine
 sufficiency to mTORC1]]
Message-ID: <20171019190902.18741771@VictoriasJourney.com>

Per A2 in questa discussione, Come usare sed / grep per estrarre il testo tra due parole? la prima espressione, di seguito, "funziona" purché il testo corrispondente non contenga una nuova riga:

grep -o -P '(?<=Subject: ).*(?=molecular)' corpus/01

[SLC38A9 lysosomal arginine sensor; mTORC1 pathway] Key

Tuttavia, nonostante abbia provato numerose varianti ( .+?; /s; ...), non sono riuscito a farle funzionare:

grep -o -P '(?<=Subject: ).*(?=link)' corpus/01
grep -o -P '(?<=Subject: ).*(?=therapeutic)' corpus/01
etc.

Soluzione 1.

Per Estrai il testo tra due stringhe su righe diverse

sed -n '/Subject: /{:a;N;/Message-ID:/!ba; s/\n/ /g; s/\s\s*/ /g; s/.*Subject: \|Message-ID:.*//g;p}' corpus/01

che dà

[SLC38A9 lysosomal arginine sensor; mTORC1 pathway] Key molecular link in major cell growth pathway: Findings point to new potential therapeutic target in pancreatic cancer [mTORC1 Activator SLC38A9 Is Required to Efflux Essential Amino Acids from Lysosomes and Use Protein as a Nutrient] [Re: Nutrient sensor in key growth-regulating metabolic pathway identified [Lysosomal amino acid transporter SLC38A9 signals arginine sufficiency to mTORC1]]                              

Soluzione 2. *

Per Come posso sostituire una nuova riga (\ n) usando sed?

sed ':a;N;$!ba;s/\n/ /g' corpus/01

sostituirà le nuove righe con uno spazio.

Concatenalo con A2 in Come usare sed / grep per estrarre il testo tra due parole? , noi abbiamo:

sed ':a;N;$!ba;s/\n/ /g' corpus/01 | grep -o -P '(?<=Subject: ).*(?=Message-ID:)'

che dà

[SLC38A9 lysosomal arginine sensor; mTORC1 pathway] Key molecular  link in major cell growth pathway: Findings point to new potential  therapeutic target in pancreatic cancer [mTORC1 Activator SLC38A9 Is  Required to Efflux Essential Amino Acids from Lysosomes and Use Protein as  a Nutrient] [Re: Nutrient sensor in key growth-regulating metabolic pathway  identified [Lysosomal amino acid transporter SLC38A9 signals arginine  sufficiency to mTORC1]] 

Questa variante rimuove i doppi spazi:

sed ':a;N;$!ba;s/\n/ /g; s/\s\s*/ /g' corpus/01 | grep -o -P '(?<=Subject: ).*(?=Message-ID:)'

dando

[SLC38A9 lysosomal arginine sensor; mTORC1 pathway] Key molecular link in major cell growth pathway: Findings point to new potential therapeutic target in pancreatic cancer [mTORC1 Activator SLC38A9 Is Required to Efflux Essential Amino Acids from Lysosomes and Use Protein as a Nutrient] [Re: Nutrient sensor in key growth-regulating metabolic pathway identified [Lysosomal amino acid transporter SLC38A9 signals arginine sufficiency to mTORC1]]

1
bella avventura :))
Alexandru-Mihai Manolescu
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.