2009-11-14 10 views
87

Ich sehe viele Beispiele und man-Seiten, wie man Dinge wie Suchen und Ersetzen mit sed, awk oder gawk macht.Wie sed, awk oder gawk verwenden, um nur das zu drucken, was passt?

Aber in meinem Fall habe ich einen regulären Ausdruck, den ich gegen eine Textdatei ausführen möchte, um einen bestimmten Wert zu extrahieren. Ich möchte nicht suchen und ersetzen. Dies wird von bash aufgerufen. Nehmen wir ein Beispiel verwenden:

Beispiel für reguläre Ausdrücke:

.*abc([0-9]+)xyz.* 

Beispiel Eingabedatei:

a 
b 
c 
abc12345xyz 
a 
b 
c 

So einfach wie das klingt, ich kann nicht herausfinden, wie sed anrufen/awk/gawk richtig . Was ich zu tun gehofft, aus meinem Bash-Skript ist haben:

myvalue=$(sed <...something...> input.txt) 

Dinge, die ich versucht habe, sind:

sed -e 's/.*([0-9]).*/\\1/g' example.txt # extracts the entire input file 
sed -n 's/.*([0-9]).*/\\1/g' example.txt # extracts nothing 
+5

Wow ... Leute wählten diese Frage nach -1? Ist das wirklich unpassend für eine Frage? –

+0

Es scheint vollkommen angemessen, Regex und leistungsfähige Befehlszeilenprogramme wie sed/awk oder irgendein Editor wie vi, emacs oder teco zu verwenden, kann mehr wie Programmierung sein, als nur einige ol Anwendung zu verwenden. IMO das gehört auf SO mehr als SU. – Dereleased

+0

Vielleicht wurde es abgelehnt, weil es in seiner ursprünglichen Form einige Anforderungen nicht klar definiert hat. Es tut es immer noch nicht, es sei denn, Sie haben die Kommentare des OP zu den Antworten gelesen (einschließlich der, die ich gelöscht habe, als die Dinge in Birnenform verliefen). – pavium

Antwort

38

Mein sed (Mac OS X) nicht mit + arbeiten.Ich habe versucht, * statt und ich fügte p Tag für Druck-Spiel:

sed -n 's/^.*abc\([0-9]*\)xyz.*$/\1/p' example.txt 

Für mindestens ein numerisches Zeichen ohne + passend, ich würde verwenden:

sed -n 's/^.*abc\([0-9][0-9]*\)xyz.*$/\1/p' example.txt 
+0

Danke, das hat auch für mich funktioniert, sobald ich * statt + benutzt habe. –

+2

... und die Option "p", um das Spiel auszudrucken, was ich auch nicht wusste. Danke noch einmal. –

+2

Ich musste dem '+' entkommen und dann funktionierte es für mich: 'sed -n 's /^.* abc \ ([0-9] \ + \) xyz. * $/\ 1/p'' –

15

Ich benutze perl, um diese leichter für mich zu machen. z.B.

perl -ne 'print $1 if /.*abc([0-9]+)xyz.*/' 

Dies läuft Perl, die -n Option weist Perl in einer Zeile in einer Zeit von STDIN zu lesen und den Code auszuführen. Die Option -e gibt die auszuführende Anweisung an.

Die Anweisung führt eine Regexp auf der Zeile lesen, und wenn es übereinstimmt, druckt den Inhalt der ersten Reihe von Bracks ($1).

Sie können dies mehrere Dateinamen am Ende auch tun. z.B.

perl -ne 'print $1 if /.*abc([0-9]+)xyz.*/' example1.txt example2.txt

+0

Danke, aber wir haben keinen Zugriff auf Perl, weshalb ich nach sed/awk/gawk gefragt habe. –

1

Wenn Sie Zeilen auswählen möchten, dann die Bits Streifen aus Sie nicht wollen:

egrep 'abc[0-9]+xyz' inputFile | sed -e 's/^.*abc//' -e 's/xyz.*$//' 

Er wählt grundsätzlich die Zeilen, die Sie mit egrep wollen und verwendet dann sed abzustreifen die Bits vor und nach der Nummer.

Sie können dies in Aktion sehen hier:

pax> echo 'a 
b 
c 
abc12345xyz 
a 
b 
c' | egrep 'abc[0-9]+xyz' | sed -e 's/^.*abc//' -e 's/xyz.*$//' 
12345 
pax> 

Update: offensichtlich, wenn Sie aktuelle Situation komplexer ist, werden die REs müssen mir geändert. Wenn Sie zum Beispiel schon immer eine einzige Nummer begraben innerhalb null oder mehr Nicht-Numerik am Anfang und Ende:

egrep '[^0-9]*[0-9]+[^0-9]*$' inputFile | sed -e 's/^[^0-9]*//' -e 's/[^0-9]*$//' 
+0

Interessant ... Es gibt also keine einfache Möglichkeit, einen komplexen regulären Ausdruck anzuwenden und genau das zurückzubekommen, was sich im Abschnitt (...) befindet. Denn während ich sehe, was du hier zuerst mit grep gemacht hast, dann mit sed, ist unsere reale Situation viel komplexer als das Fallenlassen von "abc" und "xyz". Der reguläre Ausdruck wird verwendet, weil auf beiden Seiten des Textes, den ich extrahieren möchte, viel unterschiedlicher Text erscheinen kann. –

+0

Ich bin sicher, es gibt * einen besseren Weg, wenn die REs wirklich komplex sind. Vielleicht könnten Sie, wenn Sie uns ein paar Beispiele oder eine detailliertere Beschreibung geben, unsere Antworten anpassen. – paxdiablo

-3

Für awk. Ich würde das folgende Skript verwenden:

/.*abc([0-9]+)xyz.*/ { 
      print $0; 
      next; 
      } 
      { 
      /* default, do nothing */ 
      } 
+0

was grep wie Verhalten bekommt ... – dmckee

+0

Dies gibt nicht den numerischen Wert '([0-9 +])' aus, dies gibt die gesamte Zeile aus. –

-3
gawk '/.*abc([0-9]+)xyz.*/' file 
+2

Das scheint nicht zu funktionieren. Es druckt die gesamte Zeile anstelle der Übereinstimmung. –

+0

In Ihrer Beispiel-Eingabedatei ist dieses Muster die gesamte Zeile. Recht??? Wenn Sie wissen, dass das Muster in einem bestimmten Feld sein wird: Verwenden Sie $ 1, $ 2 etc .. zB gawk '$ 1 ~ /.*abc([0-9]+)xyz.*/'Datei – ghostdog74

5

Wenn Ihre Version von grep unterstützt Sie die -o Option könnten nur der Teil jeder Zeile zu drucken, die Ihren regexp paßt.

Wenn nicht, dann ist hier die beste sed ich tun konnte mit:

sed -e '/[0-9]/!d' -e 's/^[^0-9]*//' -e 's/[^0-9]*$//' 

..., die löscht/ohne Ziffern überspringt und, für die übrigen Linien, entfernt alle führenden und nachlauf nicht-nummerische Zeichen . (Ich vermute nur, dass Sie die Nummer aus jeder Zeile extrahieren wollen).

Das Problem mit so etwas wie:.

sed -e 's/.*\([0-9]*\).*/&/' 

.... oder

sed -e 's/.*\([0-9]*\).*/\1/' 

... ist, dass sed nur "gierig" Spiel unterstützt ... so die erste * wird Finde den Rest der Zeile. Wenn wir nicht eine negierte Zeichenklasse verwenden können, um eine nicht-gierige Übereinstimmung zu erzielen ... oder eine Version von sed mit Perl-kompatiblen oder anderen Erweiterungen ihrer regulären Ausdrücke, können wir keine genaue Musterübereinstimmung mit dem Musterraum extrahieren (a Linie).

+0

Sie können einfach zwei Ihrer' sed'-Befehle auf diese Weise kombinieren: 'sed -n 's/[^ 0-9] * \ ([0-9] \ + \). */\ 1/p'' –

+0

Bisher wusste ich nicht über -o Option auf grep. Gut zu wissen. Aber es druckt das ganze Spiel, nicht das "(...)". Wenn Sie also auf "abc ([[: digit:]]] +) xyz" abgleichen, erhalten Sie sowohl "abc" als auch "xyz" sowie die Ziffern. –

-1

Sie können es mit der Schale

while read -r line 
do 
    case "$line" in 
     *abc*[0-9]*xyz*) 
      t="${line##abc}" 
      echo "num is ${t%%xyz}";; 
    esac 
done <"file" 
2

perl die sauberste Syntax ist, aber wenn Sie nicht über Perl (nicht immer da, ich verstehe), dann der einzige Weg, gaffen und Komponenten zu verwenden, einer Regex ist die Verwendung der Gensub-Funktion.

gawk '/abc[0-9]+xyz/ { print gensub(/.*([0-9]+).*/,"\\1","g"); }' < file 

Ausgang der Eingabedatei wird

12345 

Hinweis sein. Gensub ersetzt die gesamte Regex (zwischen //), so müssen Sie die setzen * vor und nach dem ([ 0-9] +), um den Text vor und nach der Zahl in der Ersetzung zu löschen.

+2

Eine clevere, praktikable Lösung, wenn Sie gawk benutzen müssen (oder wollen). Sie haben dies zur Kenntnis genommen, aber um es klar zu stellen: Nicht-GNU awk hat gensub() nicht und unterstützt dies daher nicht. – cincodenada

+0

Schön! Es ist jedoch am besten, 'match()' zu verwenden, um auf die erfassten Gruppen zuzugreifen. Siehe hierzu [meine Antwort] (http://stackoverflow.com/a/39075261/1983854). – fedorqui

28

können Sie sed verwenden diese

sed -rn 's/.*abc([0-9]+)xyz.*/\1/gp' 
  • -n zu tun, nicht drucken die resultierende Linie
  • -r das es Sie macht also nicht die der Capture-Gruppe () parens entkommen.
  • \1 der Capture-Gruppenspiel
  • /g global Spiel
  • /p drucken das Ergebnis

ich ein tool für mich schrieb, dass dies einfacher

rip 'abc(\d+)xyz' '$1' 
+2

Dies ist bei weitem die beste und am besten erklärte Antwort bis jetzt! –

+0

Mit einigen Erklärungen ist es viel besser zu verstehen, was mit unserem Problem nicht stimmt. Vielen Dank ! – r4phG

3

Sie awk mit match() macht verwenden können um auf die erfasste Gruppe zuzugreifen:

$ awk 'match($0, /abc([0-9]+)xyz/, matches) {print matches[1]}' file 
12345 

Dies versucht, das Muster abc[0-9]+xyz zu entsprechen. Wenn dies der Fall ist, speichert es seine Schichten in dem Array matches, dessen erstes Element der Block [0-9]+ ist. Seit match()gibt die Zeichenposition oder den Index der Stelle zurück, an der der Teilstring beginnt (1, wenn er am Anfang des Strings beginnt), löst die Aktion print aus.


Mit grep können Sie einen Blick hinter verwenden und Look-Ahead:

$ grep -oP '(?<=abc)[0-9]+(?=xyz)' file 
12345 

$ grep -oP 'abc\K[0-9]+(?=xyz)' file 
12345 

Dies prüft das Muster [0-9]+, wenn es innerhalb abc und xyz auftritt und druckt nur die Ziffern.

Verwandte Themen