Wenn awk / pattern / {print “text”} / patern / {print “”} verwendet wird, gibt es ein anderes Muster?

22

Angenommen, ich habe eine Textdatei wie:

R1 12 324 3453 36 457 4 7 8
R2 34 2342 2525 25 25 26 26 2 2
R3 23 2342 32 52 54 543 643 63
R4 25 234 2342 4 234242

Ich möchte verwenden awk, um diese Zeilen anders zu verarbeiten, wie z

awk '/R1/ { print "=>" $0} /R2/ { print "*" $0} '

und ich möchte auch alle übrigen Zeilen so drucken, wie sie sind (ohne Duplikate der bereits verarbeiteten Zeilen anzufertigen). Grundsätzlich benötige ich eine /ELSE/ { print $0}am Ende meiner awkZeile.

Gibt es so etwas?

Ali
quelle

Antworten:

27

Vereinfachter Ansatz mit awk

awk '/R1/ {print "=>" $0;next} /R2/{print "*" $0;next} 1' text.file

[jaypal:~/Temp] cat text.file 
R1 12 324 3453 36 457 4 7 8
R2 34 2342 2525 25 25 26 26 2 2
R3 23 2342 32 52 54 543 643 63
R4 25 234 2342 4 234242

[jaypal:~/Temp] awk '/R1/ { print "=>" $0;next} /R2/{print "*" $0;next}1' text.file
=>R1 12 324 3453 36 457 4 7 8
*R2 34 2342 2525 25 25 26 26 2 2
R3 23 2342 32 52 54 543 643 63
R4 25 234 2342 4 234242
[jaypal:~/Temp] 

Breakout of Pattern {Action} -Anweisungen:

  • /R1/ { print "=>" $0;next}: Dies bedeutet, dass Zeilen mit /R1/der Aktion Drucken ausgeführt =>werden. nextbedeutet, dass der Rest der awk-Anweisungen ignoriert und die nächste Zeile betrachtet wird.

  • /R2/{print "*" $0;next}: Dies bedeutet, dass Zeilen, die pattern /R2/der Aktion des Druckens *entsprechen, ausgeführt werden. Wenn die awkVerarbeitung beginnt, wird die erste pattern {action}Anweisung ignoriert, da pattern /R1/dies für Zeilen mit nicht zutrifft /R2/. Die zweite pattern {action}Anweisung wird also in der Zeile ausgeführt. nextwürde wiederum bedeuten, dass wir keine weitere Verarbeitung mehr wünschen und awkordnungsgemäß zur nächsten Zeile übergehen.

  • 1druckt alle Zeilen. Wenn nur eine Bedingung mit no geliefert wird {action}, verwendet awk standardmäßig {print}. Hier ist die Bedingung, 1die als wahr interpretiert wird, so dass es immer gelingt. Wenn wir an diesem Punkt angelangt sind, wurde die erste und die zweite pattern {action}Anweisung ignoriert oder umgangen (für Zeilen ohne /R1/und /R2/), sodass die Standarddruckaktion für die verbleibenden Zeilen ausgeführt wird.

Jaypal Singh
quelle
Scheint von allen veröffentlichten Lösungen am schnellsten zu laufen.
Chris Down
1
Ich bin mir nicht sicher, ob syntaktischer Zucker der richtige Begriff ist ... Es ist nur Syntax.
Daniel Hershcovich
7

awkimplementiert die üblichen Verdächtigen, wenn es um Bedingungen geht. Es ist eine gute Idee, printfanstatt printfür den Job zu verwenden, den Sie auf Match erledigen möchten.

awk '{ if (/^R1/) { printf("=> %s\n", $0) } else if (/^R2/) { printf("* %s\n", $0) } else { print $0 } }'
Chris Down
quelle
Das brauchst du nicht wirklich if-then-else.
Jaypal Singh
1
Das funktioniert zwar perfekt, ist aber nicht idiomatisch. Der umsichtige Umgang mit nextist ein wichtiges Werkzeug bei der awk-Programmierung.
dmckee
2
Ich verstehe den Sinn der Verwendung printfhier nicht. Der einzige Vorteil (es sei denn, Sie machen eine schickere Formatierung als Verkettung) ist, dass keine neue Zeile hinzugefügt wird, was hier nicht relevant ist.
Gilles 'SO- hör auf böse zu sein'
1
Das ist ein kontraproduktives und überraschendes Ergebnis. Unadorned muss printnur ausgeben, $0während printfein Formatstring analysiert werden muss.
JW013
5

Chris Down hat bereits gezeigt, wie Sie mithilfe einer expliziten 'if'-Anweisung in einem Block ein else für reguläre Ausdrücke erhalten können. Sie können den gleichen Effekt auch auf andere Weise erzielen, obwohl seine Lösung wahrscheinlich besser ist.

Eine besteht darin, einen dritten regulären Ausdruck zu schreiben, der nur mit Text übereinstimmt, der nicht von den anderen übereinstimmt. In Ihrem Fall würde dies ungefähr so ​​aussehen:

awk '/^R1/ { print "=>" $0}
     /^R2/ { print "*" $0}
     /^[^R]/ || /^R[^12]/ { print $0 } '

Beachten Sie, dass dies verankerte reguläre Ausdrücke verwendet - das ^ am Anfang der regulären Ausdrücke stimmt nur mit dem Anfang einer Zeile überein - Ihre ursprünglichen Muster haben dies nicht getan, wodurch die Übereinstimmung etwas verlangsamt wird, da alle Zeichen in einer Zeile anstatt überprüft werden überspringen bis zur nächsten Zeile. Der dritte Fall ("else") stimmt mit einer Zeile überein, die mit einem Zeichen beginnt, das nicht "R" ([^ R]) ist, oder mit einem "R", gefolgt von einem Zeichen, das keine "1" oder "ist. 2 '(R [^ 12]). Die zwei unterschiedlichen Bedeutungen von ^ sind etwas verwirrend, aber dieser Fehler wurde vor langer Zeit begangen und wird in Kürze nicht mehr geändert.

Um komplementäre reguläre Ausdrücke verwenden zu können, müssen sie wirklich verankert sein, da sonst das [^ R] zB mit der darauf folgenden 1 übereinstimmt. Für sehr einfache reguläre Ausdrücke wie Sie kann dieser Ansatz nützlich sein, aber wenn die regulären Ausdrücke komplexer werden, kann dieser Ansatz nicht mehr verwaltet werden. Stattdessen können Sie Statusvariablen für jede Zeile wie folgt verwenden:

awk '{ handled = 0 }
     /^R1/ { print "=>" $0; handled = 1}
     /^R2/ { print "*" $0; handled = 1}
     { if (!handled) print $0 } '

Diese Einstellung setzt den Wert für jede neue Zeile auf Null, dann auf 1, wenn sie mit einer der beiden regulären Ausdrücke übereinstimmt, und führt schließlich, wenn sie immer noch Null ist, den Ausdruck $ 0 aus.

Alex Dupuy
quelle
Es sollte beachtet werden, dass bei großen Dateien beide weniger effizient sind als die Verwendung von Bedingungen (wie hier gezeigt ). rfileEs werden nur 10000 Zeilen des Datensatzes des Fragestellers wiederholt.
Chris Down
4
if (!handled)Yuck! Verwenden Sie nextdiese Option , um die Berücksichtigung anderer Aktionen zu beenden.
dmckee
+1 für if (!handled). Allgemeine, flexible und wiederverwendbare Lösungen sind gut. Was ist, wenn die nächste Person, die diese Frage hat, nach dem Drucken weitere Verarbeitungsschritte ausführen möchte? Die Antworten mit nextstützen das nicht.
Scott