412 Stimmen

Wie kann ich mit sed nur erfasste Gruppen ausgeben?

Gibt es eine Möglichkeit zu sagen sed um nur erfasste Gruppen auszugeben?

Zum Beispiel, wenn die Eingabe gegeben ist:

This is a sample 123 text and some 987 numbers

Und Muster:

/([\d]+)/

Könnte ich nur 123 und 987 in der durch Rückverweise formatierten Form ausgeben lassen?

491voto

Dennis Williamson Punkte 322329

Der Schlüssel zum Erfolg liegt darin, dass Sie sed um auszuschließen, was nicht ausgegeben werden soll, und um anzugeben, was Sie wollen.

string='This is a sample 123 text and some 987 numbers'
echo "$string" | sed -rn 's/[^[:digit:]]*([[:digit:]]+)[^[:digit:]]+([[:digit:]]+)[^[:digit:]]*/\1 \2/p'

Hier steht:

  • nicht standardmäßig jede Zeile ausdrucken ( -n )
  • Null oder mehr Nicht-Ziffern ausschließen
  • eine oder mehrere Ziffern enthalten
  • eine oder mehrere Nicht-Ziffern ausschließen
  • eine oder mehrere Ziffern enthalten
  • Null oder mehr Nicht-Ziffern ausschließen
  • die Ersetzung drucken ( p )

Generell gilt für sed Sie erfassen Gruppen mit Hilfe von Klammern und geben das Erfasste mit einem Rückverweis aus:

echo "foobarbaz" | sed 's/^foo\(.*\)baz$/\1/'

wird "bar" ausgegeben. Wenn Sie -r ( -E für OS X) für erweiterte Regex, müssen Sie die Klammern nicht entschlüsseln:

echo "foobarbaz" | sed -r 's/^foo(.*)baz$/\1/'

Es kann bis zu 9 Erfassungsgruppen und deren Rückverweise geben. Die Rückverweise sind in der Reihenfolge nummeriert, in der die Gruppen erscheinen, aber sie können in beliebiger Reihenfolge verwendet werden und sind wiederholbar:

echo "foobarbaz" | sed -r 's/^foo(.*)b(.)z$/\2 \1 \2/'

gibt "a bar a" aus.

Wenn Sie GNU grep (es kann auch unter BSD, einschließlich OS X, funktionieren):

echo "$string" | grep -Po '\d+'

oder Varianten wie z.B.:

echo "$string" | grep -Po '(?<=\D )(\d+)'

En -P aktiviert Perl-kompatible reguläre Ausdrücke. Siehe man 3 pcrepattern ou man 3 pcresyntax .

58voto

Peter McG Punkte 18387

Sed kann sich bis zu neun Muster merken, aber Sie müssen Teile des regulären Ausdrucks mit Hilfe von geschützten Klammern merken.

見る aquí für Beispiele und mehr Details

41voto

ghostdog74 Punkte 305138

Können Sie grep verwenden

grep -Eow "[0-9]+" file

23voto

Ziffernfolge(n)

Diese Antwort funktioniert mit jeder Anzahl von Zifferngruppen. Beispiel:

$ echo 'Num123that456are7899900contained0018166intext' \
   | sed -En 's/[^0-9]*([0-9]{1,})[^0-9]*/\1 /gp'

123 456 7899900 0018166

Ausführliche Antwort.

Gibt es eine Möglichkeit, sed anzuweisen, nur erfasste Gruppen auszugeben?

Ja. Ersetzen Sie den gesamten Text durch die Erfassungsgruppe:

$ echo 'Number 123 inside text' \
   | sed 's/[^0-9]*\([0-9]\{1,\}\)[^0-9]*/\1/'

123

s/[^0-9]*                           # several non-digits
         \([0-9]\{1,\}\)            # followed by one or more digits
                        [^0-9]*     # and followed by more non-digits.
                               /\1/ # gets replaced only by the digits.

Oder mit einer erweiterten Syntax (weniger Anführungszeichen und die Verwendung von +):

$ echo 'Number 123 in text' \
   | sed -E 's/[^0-9]*([0-9]+)[^0-9]*/\1/'

123

Um zu vermeiden, dass der Originaltext gedruckt wird, wenn es keine Nummer gibt, verwenden Sie:

$ echo 'Number xxx in text' \
   | sed -En 's/[^0-9]*([0-9]+)[^0-9]*/\1/p'
  • (-n) Standardmäßig wird die Eingabe nicht gedruckt.
  • (/p) nur drucken, wenn eine Ersetzung erfolgt ist.

Und um mehrere Zahlen abzugleichen (und sie auch auszudrucken):

$ echo 'N 123 in 456 text' \
  | sed -En 's/[^0-9]*([0-9]+)[^0-9]*/\1 /gp'

123 456

Das funktioniert für jede Anzahl von Ziffernläufen:

$ str='Test Num(s) 123 456 7899900 contained as0018166df in text'
$ echo "$str" \
   | sed -En 's/[^0-9]*([0-9]{1,})[^0-9]*/\1 /gp'

123 456 7899900 0018166

Dies ist dem Befehl grep sehr ähnlich:

$ str='Test Num(s) 123 456 7899900 contained as0018166df in text'
$ echo "$str" | grep -Po '\d+'
123
456
7899900
0018166

Über \d

und Muster: /([\d]+)/

Sed erkennt nicht die ' \d ' (Abkürzung) Syntax. Die oben verwendete ascii-Entsprechung [0-9] nicht genau gleichwertig ist. Die einzige alternative Lösung ist die Verwendung einer Zeichenklasse: '[[:digit:]]`.

Die ausgewählte Antwort verwendet solche "Charakterklassen", um eine Lösung zu finden:

$ str='This is a sample 123 text and some 987 numbers'
$ echo "$str" | sed -rn 's/[^[:digit:]]*([[:digit:]]+)[^[:digit:]]+([[:digit:]]+)[^[:digit:]]*/\1 \2/p'

Diese Lösung funktioniert nur für (genau) zwei Ziffernfolgen.

Da die Antwort innerhalb der Shell ausgeführt wird, können wir natürlich ein paar Variablen definieren, um die Antwort zu verkürzen:

$ str='This is a sample 123 text and some 987 numbers'
$ d=[[:digit:]]     D=[^[:digit:]]
$ echo "$str" | sed -rn "s/$D*($d+)$D+($d+)$D*/\1 \2/p"

Doch wie bereits erläutert, ist die Verwendung eines s/…/…/gp Befehl ist besser:

$ str='This is 75577 a sam33ple 123 text and some 987 numbers'
$ d=[[:digit:]]     D=[^[:digit:]]
$ echo "$str" | sed -rn "s/$D*($d+)$D*/\1 /gp"
75577 33 123 987

Dies gilt sowohl für wiederholte Ziffernfolgen als auch für das Schreiben eines Kurzbefehls (Short(er)).

15voto

Aufgeben und Perl verwenden

Depuis sed nicht ausreicht, werfen wir einfach das Handtuch und verwenden Perl, zumindest ist es LSB während grep GNU-Erweiterungen sind es nicht :-)

  • Druckt den gesamten übereinstimmenden Teil, keine übereinstimmenden Gruppen oder Lookbehind erforderlich:

    cat <<EOS | perl -lane 'print m/\d+/g'
    a1 b2
    a34 b56
    EOS

    Ausgabe:

    12
    3456
  • Einzelne Übereinstimmung pro Zeile, oft strukturierte Datenfelder:

    cat <<EOS | perl -lape 's/.*?a(\d+).*/$1/g'
    a1 b2
    a34 b56
    EOS

    Ausgabe:

    1
    34

    Mit Blick nach hinten:

    cat <<EOS | perl -lane 'print m/(?<=a)(\d+)/'
    a1 b2
    a34 b56
    EOS
  • Mehrere Felder:

    cat <<EOS | perl -lape 's/.*?a(\d+).*?b(\d+).*/$1 $2/g'
    a1 c0 b2 c0
    a34 c0 b56 c0
    EOS

    Ausgabe:

    1 2
    34 56
  • Mehrere Treffer pro Zeile, oft unstrukturierte Daten:

    cat <<EOS | perl -lape 's/.*?a(\d+)|.*/$1 /g'
    a1 b2
    a34 b56 a78 b90
    EOS

    Ausgabe:

    1 
    34 78

    Mit Blick nach hinten:

    cat EOS<< | perl -lane 'print m/(?<=a)(\d+)/g'
    a1 b2
    a34 b56 a78 b90
    EOS

    Ausgabe:

    1
    3478

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X