Nicht gieriges (widerstrebendes) Regex-Matching in sed?

406

Ich versuche, sed zu verwenden, um URL-Zeilen zu bereinigen und nur die Domain zu extrahieren.

Also von:

http://www.suepearson.co.uk/product/174/71/3816/

Ich möchte:

http://www.suepearson.co.uk/

(entweder mit oder ohne den abschließenden Schrägstrich, es spielt keine Rolle)

Ich habe versucht:

 sed 's|\(http:\/\/.*?\/\).*|\1|'

und (dem nicht gierigen Quantifizierer entkommen)

sed 's|\(http:\/\/.*\?\/\).*|\1|'

Aber ich kann nicht scheinen, dass der nicht gierige Quantifizierer ( ?) funktioniert, so dass er immer mit der gesamten Zeichenfolge übereinstimmt.

Joel
quelle
54
Eine Randnotiz: Wenn Sie Ihre regulären Ausdrücke mit "|" abgrenzen, müssen Sie den "/" nicht entkommen. Tatsächlich begrenzen die meisten Menschen mit "|" anstelle von "/" s, um die "Lattenzäune" ​​zu vermeiden.
AttishOculus
12
@AttishOculus Das erste Zeichen nach dem 's' in einem Ersatzausdruck in sed ist das Trennzeichen. Daher 's ^ foo ^ bar ^' oder 's! Foo! Bar!' auch arbeiten
Squidly
1
Verwenden Sie für erweiterte Regex sed -E 's.... Trotzdem kein widerstrebender Bediener.
Ondra Žižka
Keine Antwort auf den Fragentitel, sondern in diesem speziellen Fall einfache cut -d'/' -f1-3Arbeiten.
Petr Javorik

Antworten:

421

Weder der grundlegende noch der erweiterte Posix / GNU-Regex erkennen den nicht gierigen Quantifizierer. Sie benötigen eine spätere Regex. Glücklicherweise ist Perl Regex für diesen Kontext ziemlich einfach zu bekommen:

perl -pe 's|(http://.*?/).*|\1|'
Chaos
quelle
12
Verwenden Sie dazu Optionen -pi -e.
wirklich schön
11
Holy Smokes Ich kann nicht glauben, dass das funktioniert hat :-) Das einzige, was nervt, ist, dass mein Skript jetzt eine Perl-Abhängigkeit hat :-( Auf der positiven Seite hat praktisch jede Linux-Distribution Perl bereits, also wahrscheinlich kein Problem :-)
Freedom_Ben
6
@Freedom_Ben: IIRC perlist erforderlich von POSIX
MestreLion
4
@ dolphus333: "Weder der grundlegende noch der erweiterte Posix / GNU-Regex erkennt den nicht gierigen Quantifizierer" bedeutet "Sie können den nicht gierigen Quantifizierer in sed nicht verwenden".
Chaos
3
@ Sérgio es ist, wie Sie die angeforderte Sache tun, was unmöglich ist sed, mit einer Syntax, die im Grunde identisch mit der vonsed
Chaos
250

In diesem speziellen Fall können Sie die Arbeit erledigen, ohne einen nicht gierigen regulären Ausdruck zu verwenden.

Versuchen Sie diesen nicht gierigen regulären Ausdruck [^/]*anstelle von .*?:

sed 's|\(http://[^/]*/\).*|\1|g'
Gumbo
quelle
3
Wie kann man mit dieser Technik sed match non gierig zu einer Phrase machen?
user3694243
6
Das kannst du leider nicht; siehe die Antwort des Chaos .
Daniel H
Vielen Dank ... da Perl in vielen Linux-Distributionen nicht mehr in der Standardinstallationsbasis enthalten ist!
st0ne
16
sed nicht
gieriges
@DanielH Tatsächlich ist es möglich, Phrasen mit dieser Technik wie gewünscht nicht gierig abzugleichen . Es kann einige Schmerzen erfordern, eines der Muster mit ausreichender Präzision zu schreiben. Wenn Sie beispielsweise eine Schlüsselwertzuweisung in der Abfrage einer URL analysieren, müssen Sie möglicherweise die Zuweisung mithilfe von suchen ([^&=#]+)=([^&#]*). Es gibt Fälle, die auf diese Weise nicht sicher funktionieren, z. B. wenn die URL für ihren Host-Teil und den Pfadnamen analysiert wird, wobei der endgültige Schrägstrich als optional für die Erfassung ausgeschlossen gilt:^(http:\/\/.+?)/?$
Thomas Urban
121

Mit sed implementiere ich normalerweise eine nicht gierige Suche, indem ich bis zum Trennzeichen nach etwas anderem als dem Trennzeichen suche:

echo "http://www.suon.co.uk/product/1/7/3/" | sed -n 's;\(http://[^/]*\)/.*;\1;p'

Ausgabe:

http://www.suon.co.uk

das ist:

  • nicht ausgeben -n
  • suchen, Muster abgleichen, ersetzen und drucken s/<pattern>/<replace>/p
  • Verwenden Sie ;stattdessen das Suchbefehlstrennzeichen /, um die Eingabe zu vereinfachens;<pattern>;<replace>;p
  • Denken Sie daran, Übereinstimmung zwischen Klammern \(... \), später zugänglich mit \1, \2...
  • Spiel http://
  • von irgendetwas in Klammern gefolgt [], [ab/]würde bedeuten , entweder aoder boder/
  • zuerst ^in []Mitteln not, also gefolgt von irgendetwas anderem als der Sache in der[]
  • so [^/]bedeutet nichts außer /Charakter
  • *ist die vorherige Gruppe zu wiederholen, [^/]*bedeutet also Zeichen außer /.
  • Bisher sed -n 's;\(http://[^/]*\)bedeutet Suchen und Erinnern, http://gefolgt von Zeichen, außer /und merken Sie sich, was Sie gefunden haben
  • Wir möchten bis zum Ende der Domain suchen, also hören Sie beim nächsten auf /und fügen Sie /am Ende eine weitere hinzu. sed -n 's;\(http://[^/]*\)/'Wir möchten jedoch den Rest der Zeile nach dem Hinzufügen der Domain abgleichen.*
  • Jetzt ist die in Gruppe 1 ( \1) gespeicherte Übereinstimmung die Domäne. Ersetzen Sie also die übereinstimmende Zeile durch in der Gruppe gespeicherte Daten \1und drucken Sie:sed -n 's;\(http://[^/]*\)/.*;\1;p'

Wenn Sie auch nach der Domain einen Backslash einfügen möchten, fügen Sie der Gruppe einen weiteren Backslash hinzu, um Folgendes zu beachten:

echo "http://www.suon.co.uk/product/1/7/3/" | sed -n 's;\(http://[^/]*/\).*;\1;p'

Ausgabe:

http://www.suon.co.uk/
stefanB
quelle
8
In Bezug auf die letzten Änderungen: Klammern sind eine Art Klammerzeichen, daher ist es nicht falsch, sie Klammern zu nennen, insbesondere wenn Sie dem Wort mit den tatsächlichen Zeichen folgen, wie es der Autor getan hat. Außerdem ist es in einigen Kulturen die bevorzugte Verwendung, sodass das Ersetzen durch die bevorzugte Verwendung in Ihrer eigenen Kultur etwas unhöflich erscheint, obwohl ich sicher bin, dass der Editor dies nicht beabsichtigt hat. Persönlich denke ich, dass es am besten ist, rein beschreibende Namen wie runde Klammern , eckige Klammern und spitze Klammern zu verwenden .
Alan Moore
2
Ist es möglich, das Trennzeichen durch eine Zeichenfolge zu ersetzen?
Calculemus
37

sed unterstützt keinen "nicht gierigen" Operator.

Sie müssen den Operator "[]" verwenden, um "/" von der Übereinstimmung auszuschließen.

sed 's,\(http://[^/]*\)/.*,\1,'

PS Es ist kein Backslash "/" erforderlich.

andcoz
quelle
nicht wirklich. Wenn das Trennzeichen eines von vielen möglichen Zeichen sein könnte (z. B. nur eine Folge von Zahlen), wird Ihre Negationsübereinstimmung möglicherweise immer komplexer. das ist in Ordnung, aber es wäre sicherlich schön, eine Option zu haben. * nicht gierig
gesell
1
Die Frage war allgemeiner. Diese Lösungen funktionieren für URLs, aber nicht (z. B.) für meinen Anwendungsfall des Entfernens von nachgestellten Nullen. s/([[:digit:]]\.[[1-9]]*)0*/\1/würde offensichtlich nicht gut funktionieren für 1.20300. Da es sich bei der ursprünglichen Frage jedoch um URLs handelte, sollten diese in der akzeptierten Antwort erwähnt werden.
Daniel H
33

Simulation eines faulen (nicht gierigen) Quantifizierers in sed

Und alle anderen Regex-Aromen!

  1. Erstes Auftreten eines Ausdrucks finden:

    • POSIX ERE (mit -rOption)

      Regex:

      (EXPRESSION).*|.

      Sed:

      sed -r 's/(EXPRESSION).*|./\1/g' # Global `g` modifier should be on

      Beispiel (erste Ziffernfolge finden) Live-Demo :

      $ sed -r 's/([0-9]+).*|./\1/g' <<< 'foo 12 bar 34'
      12

      Wie funktioniert es ?

      Dieser Regex profitiert von einer Abwechslung |. An jeder Position versucht die Engine, die längste Übereinstimmung auszuwählen (dies ist ein POSIX-Standard, dem auch einige andere Engines folgen), was bedeutet, dass sie so lange .läuft, bis eine Übereinstimmung für gefunden wird ([0-9]+).*. Ordnung ist aber auch wichtig.

      Geben Sie hier die Bildbeschreibung ein

      Da das globale Flag gesetzt ist, versucht die Engine, Zeichen für Zeichen bis zum Ende der Eingabezeichenfolge oder unseres Ziels weiter abzugleichen. Sobald die erste und einzige Erfassungsgruppe der linken Seite des Wechsels übereinstimmt, wird auch der (EXPRESSION)Rest der Leitung sofort verbraucht .*. Wir halten jetzt unseren Wert in der ersten Erfassungsgruppe.

    • POSIX BRE

      Regex:

      \(\(\(EXPRESSION\).*\)*.\)*

      Sed:

      sed 's/\(\(\(EXPRESSION\).*\)*.\)*/\3/'

      Beispiel (erste Ziffernfolge finden):

      $ sed 's/\(\(\([0-9]\{1,\}\).*\)*.\)*/\3/' <<< 'foo 12 bar 34'
      12

      Dieser ist wie die ERE-Version, jedoch ohne Wechsel. Das ist alles. An jeder einzelnen Position versucht der Motor, eine Ziffer zu finden.

      Geben Sie hier die Bildbeschreibung ein

      Wenn festgestellt wird, werden andere Ziffern folgende verbraucht und eingefangen und der Rest der Leitung angepasst ist sofort ansonsten da *Mittel mehr oder Null ist es über die zweite Erfassungsgruppe überspringt \(\([0-9]\{1,\}\).*\)*und kommt an einem Punkt .ein einzelnes Zeichen zu entsprechen , und dieser Prozess wird fortgesetzt.

  2. Erstes Auftreten eines begrenzten Ausdrucks finden:

    Dieser Ansatz entspricht dem allerersten Auftreten einer durch Trennzeichen getrennten Zeichenfolge. Wir können es einen Stringblock nennen.

    sed 's/\(END-DELIMITER-EXPRESSION\).*/\1/; \
         s/\(\(START-DELIMITER-EXPRESSION.*\)*.\)*/\1/g'

    Eingabezeichenfolge:

    foobar start block #1 end barfoo start block #2 end

    -EDE: end

    -SDE: start

    $ sed 's/\(end\).*/\1/; s/\(\(start.*\)*.\)*/\1/g'

    Ausgabe:

    start block #1 end

    Der erste reguläre Ausdruck \(end\).*stimmt mit dem Trennzeichen für das erste Ende überein und erfasst es. Ersetzt endalle Übereinstimmungen durch die zuletzt erfassten Zeichen, die das Endtrennzeichen sind. Zu diesem Zeitpunkt ist unser Output : foobar start block #1 end.

    Geben Sie hier die Bildbeschreibung ein

    Dann wird das Ergebnis an den zweiten regulären Ausdruck übergeben \(\(start.*\)*.\)*, der der obigen POSIX BRE-Version entspricht. Es stimmt mit einem einzelnen Zeichen überein, wenn das Starttrennzeichen startnicht übereinstimmt. Andernfalls stimmt es mit dem Starttrennzeichen überein und erfasst es und stimmt mit den übrigen Zeichen überein.

    Geben Sie hier die Bildbeschreibung ein


Beantworten Sie Ihre Frage direkt

Unter Verwendung von Ansatz 2 (begrenzter Ausdruck) sollten Sie zwei geeignete Ausdrücke auswählen:

  • EDE: [^:/]\/

  • SDE: http:

Verwendungszweck:

$ sed 's/\([^:/]\/\).*/\1/g; s/\(\(http:.*\)*.\)*/\1/' <<< 'http://www.suepearson.co.uk/product/174/71/3816/'

Ausgabe:

http://www.suepearson.co.uk/

Hinweis: Dies funktioniert nicht mit identischen Trennzeichen.

revo
quelle
3) Während Sie Websites wie regex101 für die Demo vorschlagen, fügen Sie bitte einen Hinweis hinzu, dass es aufgrund von Syntax- und Funktionsunterschieden nicht immer für CLI-Tools geeignet ist
Sundeep
1
@Sundeep Danke. Ich habe all diese Zitate in einfache Anführungszeichen umgewandelt. Außerdem habe ich die am weitesten links stehende Spielregel als erwähnt angesehen. In sedund alle anderen Motoren, die der gleichen Standardreihenfolge folgen , spielen jedoch eine Rolle, wenn es um Gleichheit geht. Hat echo 'foo 1' | sed -r 's/.|([0-9]+).*/\1/g'also kein Match, echo 'foo 1' | sed -r 's/([0-9]+).*|./\1/g'tut es aber .
Revo
@Sundeep auch die Problemumgehung für begrenzte Ausdrücke funktionierte nicht für identische Start- und Endbegrenzer, für die ich einen Hinweis hinzugefügt habe.
Revo
Toller Punkt darüber, was passiert, wenn verschiedene Wechsel an derselben Stelle beginnen und dieselbe Länge haben. Vermutlich folgt dies der Reihenfolge von links nach rechts wie bei anderen Motoren. Sie müssen nachschlagen, wenn dies im Handbuch beschrieben ist
Sonnentag,
Hier gibt es jedoch einen seltsamen Fall: stackoverflow.com/questions/59683820/…
Sundeep
20

Nicht gierige Lösung für mehr als ein einzelnes Zeichen

Dieser Thread ist wirklich alt, aber ich gehe davon aus, dass die Leute ihn noch brauchen. Nehmen wir an, Sie möchten alles bis zum ersten Auftreten von töten HELLO. Das kann man nicht sagen [^HELLO]...

Eine gute Lösung besteht also aus zwei Schritten, vorausgesetzt, Sie können ein eindeutiges Wort sparen, das Sie beispielsweise in der Eingabe nicht erwarten top_sekrit.

In diesem Fall können wir:

s/HELLO/top_sekrit/     #will only replace the very first occurrence
s/.*top_sekrit//        #kill everything till end of the first HELLO

Natürlich könnten Sie mit einer einfacheren Eingabe ein kleineres Wort oder sogar ein einzelnes Zeichen verwenden.

HTH!

ishahak
quelle
4
Um es noch besser zu machen, nützlich in Situationen, in denen Sie kein nicht verwendetes Zeichen erwarten können: 1. Ersetzen Sie dieses Sonderzeichen durch wirklich unbenutztes WORT, 2. Ersetzen Sie die Endsequenz durch das Sonderzeichen, 3. Führen Sie die Suche durch, die mit einem Sonderzeichen endet. 4 . Sonderzeichen zurück ersetzen, 5. Sonderwort zurück ersetzen. Zum Beispiel möchten Sie einen gierigen Operator zwischen <hello> und </ hallo>:
Jakub
3
Hier Beispiel: echo "Suchen: <hello> fir ~ st <br> yes </ hallo> <hello> sec ~ ond </ hallo>" | sed -e "s, ~, VERYSPECIAL, g" -e "s, </ hallo>, ~, g" -e "s ,. * Find: <hello> ([^ ~] *). *, \ 1 , "-e" s, \ ~, </ hallo>, "-e" s, VERYSPECIAL, ~, "
Jakub
2
Genau. schöne Lösung. Ich würde den Kommentar umformulieren und sagen: Wenn Sie sich nicht darauf verlassen können, dass ~ nicht verwendet wird, ersetzen Sie seine aktuellen Vorkommen zuerst mit s / ~ / VERYspeciaL / g, führen Sie dann den obigen Trick aus und geben Sie das Original ~ mit s / VERYspeciaL / ~ / g zurück
Ishahak
1
Ich mag es, seltenere "Variablen" für diese Art von Dingen zu verwenden, also `würde ich stattdessen verwenden <$$>(da $$sich Ihre Prozess-ID in der Shell erweitert, obwohl Sie doppelte Anführungszeichen anstelle von einfachen Anführungszeichen verwenden müssten, und das könnte andere Teile Ihres regulären Ausdrucks beschädigen) oder, falls Unicode verfügbar ist, so etwas wie <∈∋>.
Adam Katz
Irgendwann muss man sich fragen , warum Sie nicht nur mit perloder pythonoder einer anderen Sprache statt. perlmacht dies auf eine weniger fragile Weise in einer einzigen Zeile ...
ArtOfWarfare
18

sed - nicht gieriges Matching von Christoph Sieghart

Der Trick, um in sed eine nicht gierige Übereinstimmung zu erzielen, besteht darin, alle Zeichen mit Ausnahme derjenigen zu finden, die die Übereinstimmung beenden. Ich weiß, ein Kinderspiel, aber ich habe wertvolle Minuten damit verschwendet, und Shell-Skripte sollten schließlich schnell und einfach sein. Für den Fall, dass jemand anderes es braucht:

Gieriges Matching

% echo "<b>foo</b>bar" | sed 's/<.*>//g'
bar

Nicht gieriges Matching

% echo "<b>foo</b>bar" | sed 's/<[^>]*>//g'
foobar
Gresolio
quelle
17

Dies kann mit cut erfolgen:

echo "http://www.suepearson.co.uk/product/174/71/3816/" | cut -d'/' -f1-3
Dee
quelle
9

Eine andere Möglichkeit, keinen regulären Ausdruck zu verwenden, besteht darin, die Methode "Felder / Trennzeichen" zu verwenden, z

string="http://www.suepearson.co.uk/product/174/71/3816/"
echo $string | awk -F"/" '{print $1,$2,$3}' OFS="/"
Ghostdog74
quelle
5

sed sicherlich hat seinen Platz aber dieser nicht keiner von ihnen!

Wie Dee betont hat: Verwenden Sie einfach cut. In diesem Fall ist es viel einfacher und viel sicherer. Hier ist ein Beispiel, in dem wir verschiedene Komponenten mithilfe der Bash-Syntax aus der URL extrahieren:

url="http://www.suepearson.co.uk/product/174/71/3816/"

protocol=$(echo "$url" | cut -d':' -f1)
host=$(echo "$url" | cut -d'/' -f3)
urlhost=$(echo "$url" | cut -d'/' -f1-3)
urlpath=$(echo "$url" | cut -d'/' -f4-)

gibt Ihnen:

protocol = "http"
host = "www.suepearson.co.uk"
urlhost = "http://www.suepearson.co.uk"
urlpath = "product/174/71/3816/"

Wie Sie sehen, ist dies ein viel flexiblerer Ansatz.

(alle Gutschrift an Dee)

Peterh
quelle
3
sed 's|(http:\/\/[^\/]+\/).*|\1|'
Lucero
quelle
1
Wenn Sie "|" verwenden Als Trennzeichen müssen Sie nicht "/" entkommen.
Michael Back
3

sed -E interpretiert reguläre Ausdrücke als erweiterte (moderne) reguläre Ausdrücke

Update: -E unter MacOS X, -r in GNU sed.

stepancheg
quelle
4
Nein, tut es nicht ... Zumindest nicht GNU sed.
Michel de Ruiter
7
Im weiteren Sinne -Eist BSD sedund damit OS X einzigartig. Links zu Manpages. -rbringt erweiterte reguläre Ausdrücke zu GNU,sed wie in der Korrektur von @ stephancheg angegeben. Seien Sie vorsichtig, wenn Sie einen Befehl mit bekannter Variabilität über Nix-Verteilungen verwenden. Das habe ich auf die harte Tour gelernt.
fny
1
Dies ist die richtige Antwort, wenn Sie sed verwenden möchten, und gilt am besten für die ursprüngliche Frage.
Will Tice
8
Die -rOption von GNU sed ändert nur die Escape-Regeln gemäß Appendix A Extended regular expressionsder Info-Datei und einigen Schnelltests. es fügt eigentlich kein nicht GNU sed version 4.2.1
gieriges
1
GNU sed wurde -Efür eine Weile als undokumentierte Option erkannt , aber in Version 4.2.2.177 wurde die Dokumentation aktualisiert, um dies widerzuspiegeln. Daher-E ist dies für beide jetzt in Ordnung.
Benjamin W.
3

Es besteht immer noch die Hoffnung, dies mit reinem (GNU) sed zu lösen. Obwohl dies in einigen Fällen keine generische Lösung ist, können Sie "Schleifen" verwenden, um alle unnötigen Teile der Zeichenfolge wie folgt zu entfernen:

sed -r -e ":loop" -e 's|(http://.+)/.*|\1|' -e "t loop"
  • -r: Verwenden Sie einen erweiterten regulären Ausdruck (für + und nicht geklammerte Klammern).
  • ": loop": Definieren Sie eine neue Bezeichnung mit dem Namen "loop".
  • -e: füge Befehle zu sed hinzu
  • "t loop": Springt zurück zum Label "loop", wenn eine erfolgreiche Ersetzung stattgefunden hat

Das einzige Problem hierbei ist, dass auch das letzte Trennzeichen ('/') abgeschnitten wird. Wenn Sie es jedoch wirklich benötigen, können Sie es nach Beendigung der "Schleife" einfach wieder zurücksetzen. Fügen Sie einfach diesen zusätzlichen Befehl am Ende des vorherigen hinzu Befehlszeile:

-e "s,$,/,"
mTUX
quelle
2

Versuchen Sie eine Gruppierung, da Sie ausdrücklich angegeben haben, dass Sie versuchen, sed zu verwenden (anstelle von Perl, Cut usw.). Dies umgeht die nicht gierige Kennung, die möglicherweise nicht erkannt wird. Die erste Gruppe ist das Protokoll (dh 'http: //', 'https: //', 'tcp: //' usw.). Die zweite Gruppe ist die Domäne:

Echo "http://www.suon.co.uk/product/1/7/3/" | sed "s | ^ \ (. * // \) \ ([^ /] * \). * $ | \ 1 \ 2 |"

Wenn Sie mit Gruppierung nicht vertraut sind, beginnen Sie hier .

BrianB
quelle
1

Mir ist klar, dass dies ein alter Eintrag ist, aber jemand kann ihn nützlich finden. Da der vollständige Domainname eine Gesamtlänge von 253 Zeichen nicht überschreiten darf, ersetzen Sie. * Durch. \ {1, 255 \}

Iain Henderson
quelle
1

Auf diese Weise können Sie mit sed einen nicht gierigen Abgleich von Zeichenfolgen mit mehreren Zeichen durchführen. Nehmen wir an, Sie möchten jeden ändern foo...bar, <foo...bar>zum Beispiel diese Eingabe:

$ cat file
ABC foo DEF bar GHI foo KLM bar NOP foo QRS bar TUV

sollte diese Ausgabe werden:

ABC <foo DEF bar> GHI <foo KLM bar> NOP <foo QRS bar> TUV

Dazu konvertieren Sie foo und bar in einzelne Zeichen und verwenden dann die Negation dieser Zeichen zwischen ihnen:

$ sed 's/@/@A/g; s/{/@B/g; s/}/@C/g; s/foo/{/g; s/bar/}/g; s/{[^{}]*}/<&>/g; s/}/bar/g; s/{/foo/g; s/@C/}/g; s/@B/{/g; s/@A/@/g' file
ABC <foo DEF bar> GHI <foo KLM bar> NOP <foo QRS bar> TUV

In obigem:

  1. s/@/@A/g; s/{/@B/g; s/}/@C/gkonvertiert {und }in Platzhalterzeichenfolgen, die in der Eingabe nicht vorhanden sein können, sodass diese Zeichen dann zum Konvertieren foound barin verfügbar sind .
  2. s/foo/{/g; s/bar/}/gkonvertiert foound barzu {und }jeweils
  3. s/{[^{}]*}/<&>/gführt die Operation aus, die wir wollen - konvertieren foo...barzu<foo...bar>
  4. s/}/bar/g; s/{/foo/gkonvertiert {und }zurück zu foound bar.
  5. s/@C/}/g; s/@B/{/g; s/@A/@/g konvertiert die Platzhalterzeichenfolgen zurück in ihre ursprünglichen Zeichen.

Beachten Sie, dass das oben Gesagte nicht davon abhängt, dass eine bestimmte Zeichenfolge nicht in der Eingabe vorhanden ist, da diese Zeichenfolgen im ersten Schritt hergestellt werden, und es auch nicht wichtig ist, mit welchem ​​Vorkommen eines bestimmten regulären Ausdrucks Sie übereinstimmen möchten, da Sie ihn {[^{}]*}so oft wie nötig verwenden können im Ausdruck, um die gewünschte tatsächliche Übereinstimmung und / oder mit dem numerischen Übereinstimmungsoperator seds zu isolieren, z. B. um nur das zweite Vorkommen zu ersetzen:

$ sed 's/@/@A/g; s/{/@B/g; s/}/@C/g; s/foo/{/g; s/bar/}/g; s/{[^{}]*}/<&>/2; s/}/bar/g; s/{/foo/g; s/@C/}/g; s/@B/{/g; s/@A/@/g' file
ABC foo DEF bar GHI <foo KLM bar> NOP foo QRS bar TUV
Ed Morton
quelle
1

Haben Sie diese Antwort noch nicht gesehen? So können Sie dies mit vioder tun vim:

vi -c '%s/\(http:\/\/.\{-}\/\).*/\1/ge | wq' file &>/dev/null

Dadurch wird die vi :%sErsetzung global ausgeführt (nachfolgend g), es wird kein Fehler eausgelöst, wenn das Muster nicht gefunden wird ( ), und die resultierenden Änderungen werden auf der Festplatte gespeichert und beendet. Die &>/dev/nullverhindert , dass die GUI von kurz auf dem Bildschirm blinkt, die lästig sein kann.

Ich mag mit einem visuper kompliziert Regexes manchmal, weil (1) perl ist tot Sterben, (2) vim hat einen sehr fortschrittlichen Regex - Engine, und (3) Ich bin bereits vertraut mit viregulären Ausdrücken in meiner Tag zu Tag Nutzung Bearbeitung Unterlagen.

Luke Davis
quelle
0
echo "/home/one/two/three/myfile.txt" | sed 's|\(.*\)/.*|\1|'

Mach dir keine Sorgen, ich habe es in einem anderen Forum bekommen :)

Dee
quelle
4
so erhalten Sie gierig Spiel: /home/one/two/three/Wenn Sie eine andere hinzufügen , /wie /home/one/two/three/four/myfile.txtSie Gierig passen fourauch: /home/one/two/three/fourdie Frage ist über nicht-gierig
StefanB
0

sed 's|\(http:\/\/www\.[a-z.0-9]*\/\).*|\1| funktioniert auch

GL2014
quelle
0

Folgendes können Sie mit einem zweistufigen Ansatz und awk tun:

A=http://www.suepearson.co.uk/product/174/71/3816/  
echo $A|awk '  
{  
  var=gensub(///,"||",3,$0) ;  
  sub(/\|\|.*/,"",var);  
  print var  
}'  

Ausgabe: http://www.suepearson.co.uk

Ich hoffe, das hilft!

VINAY NAIR
quelle
0

Eine andere sed Version:

sed 's|/[:alnum:].*||' file.txt

Es werden Übereinstimmungen /gefolgt von einem alphanumerischen Zeichen (also kein weiterer Schrägstrich) sowie den restlichen Zeichen bis zum Ende der Zeile angezeigt. Danach ersetzt es es durch nichts (dh löscht es.)

Bergahorn
quelle
1
Ich denke es sollte "[[:alnum:]]"nicht sein "[:alphanum:]".
oli_arborum