it-swarm.com.de

Wie kann ich mit sed nur erfasste Gruppen ausgeben?

Gibt es eine Möglichkeit, sed anzuweisen, nur erfasste Gruppen auszugeben? Zum Beispiel bei der Eingabe:

This is a sample 123 text and some 987 numbers

und Muster:

/([\d]+)/

Könnte ich nur eine Ausgabe von 123 und 987 erhalten, die durch Rückverweise formatiert ist?

248
Pablo

Der Schlüssel, um dies zum Laufen zu bringen, ist, sed mitzuteilen, dass ausgeschlossen werden soll, was nicht ausgegeben werden soll, und anzugeben, was gewünscht wird.

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

Das sagt:

  • drucke nicht standardmäßig jede Zeile (-n)
  • schließen Sie keine oder mehrere Nicht-Ziffern aus
  • eine oder mehrere Ziffern enthalten
  • schließen Sie eine oder mehrere Nicht-Ziffern aus
  • eine oder mehrere Ziffern enthalten
  • schließen Sie keine oder mehrere Nicht-Ziffern aus
  • ersetzung drucken (p)

Im Allgemeinen erfassen Sie in sed Gruppen in Klammern und geben das, was Sie erfassen, mit einem Rückverweis aus:

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

wird "bar" ausgeben. Wenn du benutzt -r (-E für OS X) Für erweiterte reguläre Ausdrücke müssen Sie die Klammern nicht schließen:

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

Es können bis zu 9 Erfassungsgruppen und ihre Rückverweise vorhanden sein. Die Rückverweise sind in der Reihenfolge nummeriert, in der die Gruppen angezeigt werden. Sie können jedoch in beliebiger Reihenfolge verwendet und wiederholt werden:

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

gibt "a bar a" aus.

Wenn Sie GNU grep haben (es funktioniert möglicherweise auch unter BSD, einschließlich OS X):

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

oder Variationen wie:

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

Das -P Option aktiviert Perl-kompatible reguläre Ausdrücke. Sehen man 3 pcrepattern oder man 3 pcresyntax .

292

Sed hat bis zu neun gespeicherte Muster, aber Sie müssen maskierte Klammern verwenden, um Teile des regulären Ausdrucks zu speichern.

Siehe hier für Beispiele und mehr Details

52
Peter McG

sie können Grep verwenden

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

Gib auf und benutze Perl

Da sed es nicht schneidet, werfen wir einfach das Handtuch und verwenden Perl, zumindest ist es LSB while grep GNU Erweiterungen gibt es nicht :-)

  • Drucken Sie das gesamte übereinstimmende Teil, ohne dass dazu passende Gruppen oder Lookbehind erforderlich sind:

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

    Ausgabe:

    12
    3456
    
  • Einzelne Übereinstimmung pro Zeile, häufig strukturierte Datenfelder:

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

    Ausgabe:

    1
    34
    

    Mit lookbehind:

    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 Übereinstimmungen pro Zeile, häufig unstrukturierte Daten:

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

    Ausgabe:

    1 
    34 78
    

    Mit lookbehind:

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

    Ausgabe:

    1
    3478
    

Ich glaube, dass das in der Frage angegebene Muster nur als Beispiel diente und das Ziel darin bestand, mit jedem Muster übereinzustimmen.

Wenn Sie eine sed mit der Erweiterung GNU) haben, die das Einfügen einer neuen Zeile in den Musterraum ermöglicht, ist ein Vorschlag:

> set string = "This is a sample 123 text and some 987 numbers"
>
> set pattern = "[0-9][0-9]*"
> echo $string | sed "s/$pattern/\n&\n/g" | sed -n "/$pattern/p"
123
987
> set pattern = "[a-z][a-z]*"
> echo $string | sed "s/$pattern/\n&\n/g" | sed -n "/$pattern/p"
his
is
a
sample
text
and
some
numbers

Diese Beispiele sind mit tcsh (yes, I know es ist die falsche Shell) mit CYGWIN. (Bearbeiten: Entfernen Sie für bash set und die Leerzeichen um =.)

8
Joseph Quinsey

lauf (e) von Ziffern

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

Erweiterte 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 erweiterter Syntax (weniger Anführungszeichen und Verwendung von +):

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

Verwenden Sie Folgendes, um zu vermeiden, dass der Originaltext gedruckt wird, wenn keine Nummer vorhanden ist:

$ echo 'Number xxx in text' | sed -En 's/[^0-9]*([0-9]+)[^0-9]*/\1/p'
  • (-n) Die Eingabe wird standardmäßig nicht gedruckt.
  • (/ p) Nur drucken, wenn ein Ersatz durchgeführt wurde.

Und um mehrere Zahlen zu finden (und 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

Welches dem grep-Befehl sehr ähnlich ist:

$ 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 die '\ d'-Syntax (Abkürzung) nicht. Das oben verwendete ASCII-Äquivalent [0-9] Ist nicht genau gleichwertig. Die einzige alternative Lösung ist die Verwendung einer Zeichenklasse: "[[: digit:]]".

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

$ 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.

Während die Antwort in der Shell ausgeführt wird, können wir natürlich einige Variablen definieren, um diese 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"

Wie bereits erläutert, ist es jedoch besser, den Befehl s/…/…/gp Zu verwenden:

$ 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 deckt sowohl wiederholte Ziffernfolgen als auch das Schreiben eines kurzen (er) Befehls ab.

7
Arrow

Versuchen

sed -n -e "/[0-9]/s/^[^0-9]*\([0-9]*\)[^0-9]*\([0-9]*\)[^0-9]*\([0-9]*\)[^0-9]*\([0-9]*\)[^0-9]*\([0-9]*\)[^0-9]*\([0-9]*\)[^0-9]*\([0-9]*\)[^0-9]*\([0-9]*\)[^0-9]*\([0-9]*\).*$/\1 \2 \3 \4 \5 \6 \7 \8 \9/p"

Das habe ich unter cygwin bekommen:

$ (echo "asdf"; \
   echo "1234"; \
   echo "asdf1234adsf1234asdf"; \
   echo "1m2m3m4m5m6m7m8m9m0m1m2m3m4m5m6m7m8m9") | \
  sed -n -e "/[0-9]/s/^[^0-9]*\([0-9]*\)[^0-9]*\([0-9]*\)[^0-9]*\([0-9]*\)[^0-9]*\([0-9]*\)[^0-9]*\([0-9]*\)[^0-9]*\([0-9]*\)[^0-9]*\([0-9]*\)[^0-9]*\([0-9]*\)[^0-9]*\([0-9]*\).*$/\1 \2 \3 \4 \5 \6 \7 \8 \9/p"

1234
1234 1234
1 2 3 4 5 6 7 8 9
$
5
Bert F

Es ist nicht das, wonach das OP gefragt hat (Gruppen erfassen), aber Sie können die Zahlen folgendermaßen extrahieren:

S='This is a sample 123 text and some 987 numbers'
echo "$S" | sed 's/ /\n/g' | sed -r '/([0-9]+)/ !d'

Gibt folgendes an:

123
987
2
Thomas Bratt