Wie verwende ich sed / grep, um Text zwischen zwei Wörtern zu extrahieren?

133

Ich versuche, eine Zeichenfolge auszugeben, die alles zwischen zwei Wörtern einer Zeichenfolge enthält:

Eingang:

"Here is a String"

Ausgabe:

"is a"

Verwenden von:

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

enthält die Endpunkte, aber ich möchte sie nicht einschließen.

user1190650
quelle
8
Was sollte das Ergebnis sein, wenn die Eingabe ist Here is a Here String? Oder I Hereby Dub Thee Sir Stringy?
Ghoti
5
Zu Ihrer Information. Ihr Befehl bedeutet, alles zwischen der Zeile mit dem Wort Here und der Zeile mit dem Wort String zu drucken - nicht mit dem, was Sie möchten.
Hai Vu

Antworten:

108
sed -e 's/Here\(.*\)String/\1/'
Brian Campbell
quelle
2
Vielen Dank! Was wäre, wenn ich alles zwischen "one is" und "String" in "Here is a one is a String" finden wollte? (sed -e 's / one ist (. *) String / \ 1 /'?
user1190650
5
@ user1190650 Das würde funktionieren, wenn Sie auch das "Hier ist ein" sehen möchten. Sie können es testen : echo "Here is a one is a String" | sed -e 's/one is\(.*\)String/\1/'. Wenn Sie nur den Teil zwischen "one is" und "String" haben möchten, müssen Sie den regulären Ausdruck so einstellen, dass er mit der gesamten Zeile übereinstimmt : sed -e 's/.*one is\(.*\)String.*/\1/'. s/pattern/replacement/Sagen Sie in sed "Ersetzen Sie" Muster "durch" Muster "in jeder Zeile". Es wird nur alles geändert, was mit "Muster" übereinstimmt. Wenn Sie also möchten, dass es die gesamte Zeile ersetzt, müssen Sie "Muster" an die gesamte Zeile anpassen.
Brian Campbell
9
Dies bricht ab, wenn die Eingabe istHere is a String Here is a String
Jay D
1
Es wäre toll, die Lösung für einen Fall zu sehen: "Hier ist ein bla bla String Hier ist 1 ein bla bla String Hier ist 2 ein blash blash String" Die Ausgabe sollte nur den ersten Teilstring zwischen Here und String aufnehmen "
Jay D
1
@JayD sed unterstützt kein nicht gieriges Matching. In dieser Frage finden Sie einige empfohlene Alternativen.
Brian Campbell
179

GNU grep kann auch positive und negative Vorausschau und Rückschau unterstützen: Für Ihren Fall wäre der Befehl:

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

Wenn es mehrere Vorkommen von Hereund gibt string, können Sie auswählen, ob Sie zwischen dem ersten Hereund dem letzten stringübereinstimmen möchten oder ob Sie sie einzeln abgleichen möchten . In Bezug auf Regex wird es als gieriges Match (erster Fall) oder nicht gieriges Match (zweiter Fall) bezeichnet.

$ 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 
anishsane
quelle
31
Beachten Sie, dass die -POption von GNU grep in der grepin * BSD enthaltenen oder mit SVR4 (Solaris usw.) gelieferten Option nicht vorhanden ist . In FreeBSD können Sie den devel/pcrePort installieren pcregrep, der PCRE (und Look-Ahead / Behind) unterstützt. Ältere Versionen von OSX verwendeten GNU grep, wurden jedoch in OSX Mavericks -Pvon der FreeBSD-Version abgeleitet, die diese Option nicht enthält.
Ghoti
1
Hallo, wie extrahiere ich nur bestimmte Inhalte?
Durgesh Suthar
4
Das funktioniert nicht , weil , wenn Ihre Endung String „string“ mehr als einmal vorkommt, wird es das bekommt letztes Auftreten, nicht das nächste Auftreten.
Buttle Butkus
6
Im Falle von Here is a string a stringsind beide " is a " und " is a string a "gültige Antworten (ignorieren Sie die Anführungszeichen) gemäß den Fragenanforderungen. Es hängt von Ihnen ab, welche davon Sie möchten, und die Antwort kann entsprechend unterschiedlich sein. Wie auch immer, für Ihre Anforderung wird dies funktionieren:echo "Here is a string a string" | grep -o -P '(?<=Here).*?(?=string)'
anishsane
2
@BND, Sie müssen die mehrzeilige Suchfunktion von pcregrep aktivieren . echo $'Here is \na string' | grep -zoP '(?<=Here)(?s).*(?=string)'
Anishsane
57

Die akzeptierte Antwort entfernt keinen Text, der vorher Hereoder nachher sein könnte String. Dieser Wille:

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

Der Hauptunterschied ist die Zugabe von .*unmittelbar davor Hereund danach String.

Wheeler
quelle
Ihre Antwort ist vielversprechend. Ein Problem jedoch. Wie kann ich es in den zuerst gesehenen String extrahieren, wenn sich mehrere Strings in derselben Zeile befinden? Vielen Dank
Mian Asbat Ahmad
@MianAsbatAhmad Sie möchten den *Quantifizierer zwischen Hereund Stringnicht gierig (oder faul) machen. Der von sed verwendete Regex-Typ unterstützt jedoch keine Lazy-Quantifizierer ( ?unmittelbar danach .*) gemäß dieser Stackoverflow-Frage. Um einen faulen Quantifizierer zu implementieren, würden Sie normalerweise nur mit allem übereinstimmen, außer mit dem Token, mit dem Sie nicht übereinstimmen wollten. In diesem Fall gibt es jedoch nicht nur ein einzelnes Token, sondern eine ganze Zeichenfolge String.
Wheeler
Danke, ich habe die Antwort mit awk erhalten, stackoverflow.com/questions/51041463/…
Mian Asbat Ahmad
Leider funktioniert dies nicht, wenn die Zeichenfolge Zeilenumbrüche aufweist
Witalo Benicio
Das soll es nicht. .stimmt nicht mit Zeilenumbrüchen überein. Wenn Sie Zeilenumbrüche abgleichen möchten, können Sie diese durch .etwas ersetzen [\s\s].
Wheeler
35

Sie können Strings nur in Bash entfernen:

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

Und wenn Sie ein GNU-Grep haben, das PCRE enthält , können Sie eine Zusicherung mit einer Breite von Null verwenden:

$ echo "Here is a String" | grep -Po '(?<=(Here )).*(?= String)'
is a
Ghoti
quelle
Warum ist diese Methode so langsam? Wenn Sie eine große HTML-Seite mit dieser Methode entfernen, dauert es ungefähr 10 Sekunden.
Adam Johns
@AdamJohns, welche Methode? Die PCRE? PCRE ist ziemlich komplex zu analysieren, aber 10 Sekunden scheinen extrem. Wenn Sie besorgt sind, empfehle ich Ihnen, eine Frage mit Beispielcode zu stellen und zu sehen, was die Experten sagen.
Ghoti
Ich denke, es war so langsam für mich, weil es die Quelle einer sehr großen HTML-Datei in einer Variablen enthielt. Als ich Inhalte in eine Datei schrieb und dann die Datei analysierte, erhöhte sich die Geschwindigkeit dramatisch.
Adam Johns
22

Durch GNU awk,

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

grep mit -P( perl-regexp ) -Parameterunterstützung \K, die beim Verwerfen der zuvor übereinstimmenden Zeichen hilft. In unserem Fall war die zuvor übereinstimmende Zeichenfolge Hereso, dass sie aus der endgültigen Ausgabe verworfen wurde.

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

Wenn Sie möchten, dass die Ausgabe erfolgt, können is aSie Folgendes versuchen:

$ 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
Avinash Raj
quelle
Dies funktioniert nicht für : echo "Here is a string dfdsf Here is a string" | awk -v FS="(Here|string)" '{print $2}', es kehrt nur zurück is aanstatt sollte is a is a@Avinash Raj sein
alper
20

Wenn Sie eine lange Datei mit vielen mehrzeiligen Vorkommen haben, ist es hilfreich, zuerst Zahlenzeilen zu drucken:

cat -n file | sed -n '/Here/,/String/p'
Alemol
quelle
3
Vielen Dank! Dies ist die einzige Lösung, die in meinem Fall funktioniert hat (mehrzeilige Textdatei anstelle einer einzelnen Zeichenfolge ohne Zeilenumbrüche). Um es ohne Zeilennummerierung zu haben, muss natürlich die -nOption in catweggelassen werden.
Jeffrey Lebowski
... in diesem Fall catkann ganz weggelassen werden; sedweiß, wie man eine Datei oder eine Standardeingabe liest.
Tripleee
9

Dies könnte für Sie funktionieren (GNU sed):

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

Dadurch wird jede Darstellung von Text zwischen zwei Markierungen (in diesem Fall Hereund String) in einer neuen Zeile dargestellt und die neuen Zeilen im Text bleiben erhalten.

Potong
quelle
7

Alle oben genannten Lösungen weisen Mängel auf, bei denen die letzte Suchzeichenfolge an anderer Stelle in der Zeichenfolge wiederholt wird. Ich fand es am besten, eine Bash-Funktion zu schreiben.

    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"
Gary Dean
quelle
6

Sie können zwei s-Befehle verwenden

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

Funktioniert auch

$ 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 
Ivan
quelle
6

Verstehen sed Befehl , müssen wir ihn Schritt für Schritt erstellen.

Hier ist Ihr Originaltext

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

Versuchen wir, einen HereString mit der sOption ubstition in zu entfernensed

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

An diesem Punkt, ich glaube , Sie wäre in der Lage sein , zu entfernen Stringund

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

Dies ist jedoch nicht die gewünschte Ausgabe.

Verwenden Sie die -eOption, um zwei sed-Befehle zu kombinieren

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

Hoffe das hilft

Sabrina
quelle
4

Sie können verwenden \1(siehe http://www.grymoire.com/Unix/Sed.html#uh-4 ):

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

Der Inhalt in den Klammern wird als gespeichert \1.

mvairavan
quelle
Dadurch werden Zeichenfolgen entfernt, anstatt etwas dazwischen auszugeben. Versuchen Sie, "Hallo" mit "ist" im sed-Befehl zu entfernen, und es wird "Hallo a" ausgegeben
Jonathan
1

Problem. Meine gespeicherten Claws Mail-Nachrichten werden wie folgt verpackt, und ich versuche, die Betreffzeilen zu extrahieren:

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>

Gemäß A2 in diesem Thread: Wie kann man mit sed / grep Text zwischen zwei Wörtern extrahieren? Der erste Ausdruck unten "funktioniert", solange der übereinstimmende Text keine neue Zeile enthält:

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

[SLC38A9 lysosomal arginine sensor; mTORC1 pathway] Key

Trotz zahlreicher Varianten ( .+?; /s; ...) konnte ich diese jedoch nicht zum Laufen bringen:

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

Lösung 1.

Per Text zwischen zwei Zeichenfolgen in verschiedenen Zeilen extrahieren

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

was gibt

[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]]                              

Lösung 2. *

Per Wie kann ich eine neue Zeile (\ n) mit sed ersetzen?

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

ersetzt Zeilenumbrüche durch ein Leerzeichen.

Verketten Sie das mit A2 in Wie verwende ich sed / grep, um Text zwischen zwei Wörtern zu extrahieren? , wir bekommen:

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

was gibt

[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]] 

Diese Variante entfernt doppelte Leerzeichen:

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

geben

[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]]
Victoria Stuart
quelle
1
schönes Abenteuer :))
Alexandru-Mihai Manolescu