Antworten:
Der Schlüssel, um dies zum Laufen zu bringen, besteht sed
darin, anzugeben, was nicht ausgegeben werden soll, und anzugeben, was Sie möchten.
string='This is a sample 123 text and some 987 numbers'
echo "$string" | sed -rn 's/[^[:digit:]]*([[:digit:]]+)[^[:digit:]]+([[:digit:]]+)[^[:digit:]]*/\1 \2/p'
Dies sagt:
-n
)p
)Im Allgemeinen sed
erfassen Sie Gruppen in Klammern und geben das, was Sie erfassen, mithilfe einer Rückreferenz aus:
echo "foobarbaz" | sed 's/^foo\(.*\)baz$/\1/'
gibt "bar" aus. Wenn Sie -r
( -E
für OS X) für erweiterten regulären Ausdruck verwenden, müssen Sie die Klammern nicht umgehen:
echo "foobarbaz" | sed -r 's/^foo(.*)baz$/\1/'
Es können bis zu 9 Erfassungsgruppen und deren 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 haben grep
(es kann auch in BSD funktionieren, einschließlich OS X):
echo "$string" | grep -Po '\d+'
oder Variationen wie:
echo "$string" | grep -Po '(?<=\D )(\d+)'
Die -P
Option aktiviert Perl-kompatible reguläre Ausdrücke. Siehe man 3 pcrepattern
oder man
3 pcresyntax
.
sed
Beispiel beziehen und die -r
Option (oder -E
für OS X, IIRC) verwenden, müssen Sie die Klammern nicht umgehen. Der Unterschied besteht darin, dass zwischen regulären Grundausdrücken und erweiterten regulären Ausdrücken ( -r
).
Sed hat bis zu neun gespeicherte Muster, aber Sie müssen maskierte Klammern verwenden, um sich Teile des regulären Ausdrucks zu merken.
Sehen Sie hier für Beispiele und detaillierten
sed -e 's/version=\(.+\)/\1/' input.txt
Dies wird immer noch die gesamte input.txt
\+
statt schreiben +
. Und ich verstehe nicht, warum Leute -e
nur für einen sed-Befehl verwenden.
sed -e -n 's/version=\(.+\)/\1/p' input.txt
siehe: mikeplate.com/2012/05/09/…
sed -E
, die sogenannten "modernen" oder "erweiterten" regulären Ausdrücke zu verwenden, die Perl / Java / JavaScript / Go / was auch immer viel näher kommen. (Vergleiche mit grep -E
oder egrep
.) Die Standardsyntax enthält diese seltsamen Escape-Regeln und gilt als "veraltet". Weitere Informationen zu den Unterschieden zwischen den beiden finden Sie unter man 7 re_format
.
Sie können grep verwenden
grep -Eow "[0-9]+" file
o
Option - unixhelp.ed.ac.uk/CGI/man-cgi?grep : -o, --only-match Zeigt nur den Teil einer übereinstimmenden Zeile an, der mit PATTERN übereinstimmt
grep -Eow -e "[0-9]+" -e "[abc]{2,3}"
Ich weiß nicht, wie Sie diese beiden Ausdrücke in einer Zeile haben können, abgesehen von der Weiterleitung von einem vorherigen grep (was immer noch nicht funktionieren könnte, wenn eines der Muster mehr als einmal in einer Zeile übereinstimmt ).
Diese Antwort funktioniert mit einer beliebigen Anzahl von Zifferngruppen. Beispiel:
$ echo 'Num123that456are7899900contained0018166intext' |
> sed -En 's/[^0-9]*([0-9]{1,})[^0-9]*/\1 /gp'
123 456 7899900 0018166
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 die 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'
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
Welches 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
und Muster:
/([\d]+)/
Sed erkennt die Syntax '\ d' (Verknüpfung) nicht. Das oben verwendete ASCII-Äquivalent [0-9]
ist nicht genau äquivalent. Die einzige alternative Lösung besteht darin, eine Zeichenklasse zu verwenden: '[[: 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 Ziffernläufe.
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 die Verwendung eines s/…/…/gp
Befehls jedoch 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 umfasst sowohl wiederholte Ziffernläufe als auch das Schreiben eines kurzen (er) Befehls.
Ich glaube, das in der Frage angegebene Muster diente nur als Beispiel, und das Ziel war es, mit jedem Muster übereinzustimmen.
Wenn Sie ein Sed mit der GNU-Erweiterung haben, das das Einfügen einer neuen Zeile in den Musterbereich ermöglicht, lautet 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 (ja, ich weiß, es ist die falsche Shell) mit CYGWIN. (Bearbeiten: Entfernen Sie für Bash set und die Leerzeichen um =.)
+
, müssten Sie es maskieren oder die -r
Option ( -E
für OS X) verwenden. Sie können auch verwenden \{1,\}
(oder -r
oder -E
ohne die Flucht).
Gib auf und benutze Perl
Da sed
es nicht schneidet, werfen wir einfach das Handtuch und verwenden Perl, zumindest ist es LSB, während grep
GNU-Erweiterungen nicht sind :-)
Drucken Sie das gesamte passende Teil aus, es sind keine passenden Gruppen oder Looks erforderlich:
cat <<EOS | perl -lane 'print m/\d+/g'
a1 b2
a34 b56
EOS
Ausgabe:
12
3456
Einzelü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
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"
Ich habe das unter Cygwin:
$ (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
$
Es ist nicht das, wonach das OP gefragt hat (Erfassen von Gruppen), aber Sie können die Zahlen extrahieren, indem Sie:
S='This is a sample 123 text and some 987 numbers'
echo "$S" | sed 's/ /\n/g' | sed -r '/([0-9]+)/ !d'
Gibt Folgendes:
123
987
sed
erweiterte reguläre Ausdrücke mit dem-E
Flag aktiviert werden müssen.