Der AWK-Befehl stammt noch aus den frühen Unix-Tagen. Es ist Teil des POSIX-Standards und sollte auf jedem Unix-ähnlichen System verfügbar sein. Und darüber hinaus.
Obwohl AWK manchmal aufgrund seines Alters oder fehlender Funktionen im Vergleich zu einer Mehrzwecksprache wie Perl diskreditiert wird, bleibt es ein Werkzeug, das ich gerne in meiner täglichen Arbeit verwende. Manchmal zum Schreiben relativ komplexer Programme, aber auch wegen der mächtigen Einzeiler, die Sie schreiben können, um Probleme mit Ihren Datendateien zu lösen.
Genau darum geht es in diesem Artikel. In weniger als 80 Zeichen wird Ihnen gezeigt, wie Sie die AWK-Leistung nutzen können, um nützliche Aufgaben auszuführen. Dieser Artikel soll kein vollständiges AWK-Tutorial sein, aber ich habe dennoch einige grundlegende Befehle zu Beginn hinzugefügt, sodass Sie die Kernkonzepte von AWK erfassen können, selbst wenn Sie wenig oder gar keine Vorkenntnisse haben.
Meine Beispieldateien für dieses AWK-Tutorial
Alle in diesem Artikel beschriebenen Einzeiler werden mit derselben Datendatei getestet:
cat file
CREDITS,EXPDATE,USER,GROUPS
99,01 jun 2018,sylvain,team:::admin
52,01 dec 2018,sonia,team
52,01 dec 2018,sonia,team
25,01 jan 2019,sonia,team
10,01 jan 2019,sylvain,team:::admin
8,12 jun 2018,öle,team:support
17,05 apr 2019,abhishek,guest
Eine Kopie dieser Datei erhalten Sie online auf GitHub.
Vordefinierte und automatische Variablen in AWK kennen
AWK unterstützt einige vordefinierte und automatische Variablen um Ihnen beim Schreiben Ihrer Programme zu helfen. Darunter werden Sie häufig begegnen:
RS –Das Datensatztrennzeichen. AWK verarbeitet Ihre Daten Datensatz für Datensatz. Das Datensatztrennzeichen ist das Trennzeichen, das verwendet wird, um den Eingabedatenstrom in Datensätze aufzuteilen. Standardmäßig ist dies das Zeilenumbruchzeichen. Wenn Sie es also nicht ändern, ist ein Datensatz eine Zeile der Eingabedatei.
NR – Die aktuelle Eingangsdatensatznummer. Wenn Sie das standardmäßige Zeilenumbruchtrennzeichen für Ihre Datensätze verwenden, entspricht dies der aktuellen Eingabezeilennummer.
FS/OFS –Die als Feldtrennzeichen verwendeten Zeichen. Sobald AWK einen Datensatz liest, teilt es ihn basierend auf dem Wert von FS
in verschiedene Felder auf . Wenn AWK einen Datensatz auf der Ausgabe ausgibt, werden die Felder wieder zusammengefügt, diesmal jedoch unter Verwendung des OFS
Trennzeichen anstelle des FS
Separator. Normalerweise FS
und OFS
sind gleich, müssen aber nicht. „white space“ ist der Standardwert für beide.
NF – Die Anzahl der Felder im aktuellen Datensatz. Wenn Sie für Ihre Felder das standardmäßige „Leerzeichen“-Trennzeichen verwenden, stimmt dieses mit der Anzahl der Wörter im aktuellen Datensatz überein.
Es sind andere mehr oder weniger standardmäßige AWK-Variablen verfügbar, daher lohnt es sich, in Ihrem speziellen AWK-Implementierungshandbuch nach weiteren Details zu suchen. Diese Teilmenge reicht jedoch bereits aus, um interessante Einzeiler zu schreiben.
A. Grundlegende Verwendung des AWK-Befehls
1. Alle Zeilen drucken
Dieses Beispiel ist größtenteils nutzlos, aber es wird dennoch eine gute Einführung in die AWK-Syntax sein:
awk '1 { print }' file
CREDITS,EXPDATE,USER,GROUPS
99,01 jun 2018,sylvain,team:::admin
52,01 dec 2018,sonia,team
52,01 dec 2018,sonia,team
25,01 jan 2019,sonia,team
10,01 jan 2019,sylvain,team:::admin
8,12 jun 2018,öle,team:support
17,05 apr 2019,abhishek,guest
AWK-Programme bestehen aus einem oder mehreren pattern { action }
Aussagen.
Wenn für einen bestimmten Datensatz („Zeile“) der Eingabedatei, das Muster einen Wert ungleich Null auswertet (äquivalent zu „true“ in AWK), die Befehle im entsprechenden Aktionsblock werden hingerichtet. Im obigen Beispiel seit 1
eine Konstante ungleich Null ist, der { print }
Aktionsblock wird für jeden Eingabedatensatz ausgeführt.
Ein weiterer Trick ist { print }
ist der Standard-Aktionsblock, der von AWK verwendet wird, wenn Sie keinen explizit angeben. Der obige Befehl kann also verkürzt werden als:
awk 1 file
CREDITS,EXPDATE,USER,GROUPS
99,01 jun 2018,sylvain,team:::admin
52,01 dec 2018,sonia,team
52,01 dec 2018,sonia,team
25,01 jan 2019,sonia,team
10,01 jan 2019,sylvain,team:::admin
8,12 jun 2018,öle,team:support
17,05 apr 2019,abhishek,guest
Fast ebenso nutzlos wird das folgende AWK-Programm seine Eingabe verarbeiten, aber nichts für die Ausgabe erzeugen:
awk 0 file
2. Entfernen Sie einen Dateiheader
awk 'NR>1' file
99,01 jun 2018,sylvain,team:::admin
52,01 dec 2018,sonia,team
52,01 dec 2018,sonia,team
25,01 jan 2019,sonia,team
10,01 jan 2019,sylvain,team:::admin
8,12 jun 2018,öle,team:support
17,05 apr 2019,abhishek,guest
Denken Sie daran, dies ist das Äquivalent zum expliziten Schreiben von:
awk 'NR>1 { print }' file
99,01 jun 2018,sylvain,team:::admin
52,01 dec 2018,sonia,team
52,01 dec 2018,sonia,team
25,01 jan 2019,sonia,team
10,01 jan 2019,sylvain,team:::admin
8,12 jun 2018,öle,team:support
17,05 apr 2019,abhishek,guest
Dieser Einzeiler schreibt Aufzeichnungen der Eingabedatei mit Ausnahme der allerersten, da in diesem Fall die Bedingung 1>1
ist was offensichtlich nicht stimmt.
Da dieses Programm die Standardwerte für RS
verwendet , in der Praxis wird die erste Zeile der Eingabedatei verworfen.
3. Zeilen in einem Bereich drucken
Dies ist nur eine Verallgemeinerung des vorherigen Beispiels und verdient nicht viele Erklärungen, außer &&
ist der logische and
Betreiber:
awk 'NR>1 && NR < 4' file
99,01 jun 2018,sylvain,team:::admin
52,01 dec 2018,sonia,team
4. Nur-Leerraum-Zeilen entfernen
awk 'NF' file
CREDITS,EXPDATE,USER,GROUPS
99,01 jun 2018,sylvain,team:::admin
52,01 dec 2018,sonia,team
52,01 dec 2018,sonia,team
25,01 jan 2019,sonia,team
10,01 jan 2019,sylvain,team:::admin
8,12 jun 2018,öle,team:support
17,05 apr 2019,abhishek,guest
AWK teilt jeden Datensatz basierend auf dem im FS
angegebenen Feldtrennzeichen in Felder auf Variable. Das Standard-Feldtrennzeichen ist ein-oder-mehrere-Leerzeichen (auch bekannt als Leerzeichen oder Tabulatoren). Mit diesen Einstellungen enthält jeder Datensatz, der mindestens ein Nicht-Leerzeichen enthält, mindestens ein Feld.
Mit anderen Worten, der einzige Fall, in dem NF
0 („false“) ist, wenn der Datensatz nur Leerzeichen enthält. Dieser Einzeiler druckt also nur Datensätze, die mindestens ein Nicht-Leerzeichen enthalten.
5. Entfernen aller Leerzeilen
awk '1' RS='' file
CREDITS,EXPDATE,USER,GROUPS
99,01 jun 2018,sylvain,team:::admin
52,01 dec 2018,sonia,team
52,01 dec 2018,sonia,team
25,01 jan 2019,sonia,team
10,01 jan 2019,sylvain,team:::admin
8,12 jun 2018,öle,team:support
17,05 apr 2019,abhishek,guest
Dieser Einzeiler basiert auf einer obskuren POSIX-Regel, die angibt, ob der RS
auf die leere Zeichenfolge gesetzt ist, „dann werden Datensätze durch Sequenzen getrennt, die aus einem
Erwähnenswert in der POSIX-Terminologie ist, dass eine Leerzeile eine vollständig leere Zeile ist. Zeilen, die nur Leerzeichen enthalten, zählen nicht als „Leerzeichen“.
6. Felder extrahieren
Dies ist wahrscheinlich einer der häufigsten Anwendungsfälle für AWK:das Extrahieren einiger Spalten der Datendatei.
awk '{ print $1, $3}' FS=, OFS=, file
CREDITS,USER
99,sylvain
52,sonia
52,sonia
25,sonia
10,sylvain
8,öle
,
,
,
17,abhishek
Hier setze ich explizit sowohl die Eingabe- als auch die Ausgabefeldtrennzeichen auf das Komma. Wenn AWK einen Datensatz in Felder aufteilt, speichert es den Inhalt des ersten Felds in $1, den Inhalt des zweiten Felds in $2 und so weiter. Ich verwende das hier nicht, aber erwähnenswert ist, dass $0 der gesamte Datensatz ist.
In diesem Einzeiler haben Sie vielleicht bemerkt, dass ich einen Aktionsblock ohne Muster verwende. In diesem Fall wird für das Muster 1 („true“) angenommen, sodass der Aktionsblock für jeden Datensatz ausgeführt wird.
Abhängig von Ihren Anforderungen wird möglicherweise nicht das erzeugt, was wir für Leer- oder Nur-Leerzeichen-Zeilen möchten. In diesem Fall könnte diese zweite Version etwas besser sein:
awk 'NF { print $1, $3 }' FS=, OFS=, file
CREDITS,USER
99,sylvain
52,sonia
52,sonia
25,sonia
10,sylvain
8,öle
,
17,abhishek
In beiden Fällen habe ich benutzerdefinierte Werte für FS
übergeben und OFS
auf der Kommandozeile. Eine andere Möglichkeit wäre die Verwendung eines speziellen BEGIN
Block innerhalb des AWK-Programms, um diese Variablen zu initialisieren, bevor der erste Datensatz gelesen wird. Je nach Ihrem Geschmack schreiben Sie stattdessen vielleicht lieber Folgendes:
awk 'BEGIN { FS=OFS="," } NF { print $1, $3 }' file
CREDITS,USER
99,sylvain
52,sonia
52,sonia
25,sonia
10,sylvain
8,öle
,
17,abhishek
Erwähnenswert ist, dass Sie hier auch END
verwenden können Blöcke, um einige Aufgaben auszuführen, nachdem der letzte Datensatz gelesen wurde. Wie wir es gerade sehen werden. Davon abgesehen gebe ich zu, dass dies alles andere als perfekt ist, da Nur-Leerraum-Zeilen nicht elegant gehandhabt werden. Wir werden bald eine mögliche Lösung sehen, aber vorher rechnen wir etwas…
7. Spaltenweise Berechnungen durchführen
AWK unterstützt die standardmäßigen arithmetischen Operatoren. Und konvertiert Werte je nach Kontext automatisch zwischen Text und Zahlen. Sie können auch Ihre eigenen Variablen verwenden, um Zwischenwerte zu speichern. All das ermöglicht es Ihnen, kompakte Programme zu schreiben, um Berechnungen mit Datenspalten durchzuführen:
awk '{ SUM=SUM+$1 } END { print SUM }' FS=, OFS=, file
263
Oder äquivalent mit +=
Kurzsyntax:
awk '{ SUM+=$1 } END { print SUM }' FS=, OFS=, file
263
Bitte beachten Sie, dass AWK-Variablen vor der Verwendung nicht deklariert werden müssen. Es wird angenommen, dass eine undefinierte Variable die leere Zeichenfolge enthält. Was nach den AWK-Typkonvertierungsregeln gleich der 0-Nummer ist. Aufgrund dieser Funktion habe ich mich nicht darum gekümmert, den Fall explizit zu behandeln, in dem $1
enthält Text (in der Überschrift), Leerzeichen oder einfach nichts. In all diesen Fällen zählt es als 0 und stört unsere Summierung nicht. Natürlich wäre es anders, wenn ich stattdessen Multiplikationen durchführen würde. Warum würden Sie also nicht den Kommentarbereich nutzen, um eine Lösung für diesen Fall vorzuschlagen?
8. Zählen der Anzahl nicht leerer Zeilen
Das END
habe ich bereits erwähnt vorher regieren. Hier ist eine weitere mögliche Anwendung, um die Anzahl nicht leerer Zeilen in einer Datei zu zählen:
awk '/./ { COUNT+=1 } END { print COUNT }' file
9
Hier habe ich den COUNT
verwendet Variable und inkrementiert (+=1
) für jede Zeile, die mit dem regulären Ausdruck /./
übereinstimmt . Das ist jede Zeile, die mindestens ein Zeichen enthält. Schließlich wird der END-Block verwendet, um das Endergebnis anzuzeigen, nachdem die gesamte Datei verarbeitet wurde. Der Name COUNT
ist nichts Besonderes . Ich hätte Count
verwenden können , count
, n
, xxxx
oder jeder andere Name, der den AWK-Variablenbenennungsregeln entspricht
Ist dieses Ergebnis jedoch korrekt? Nun, es hängt von Ihrer Definition einer „leeren“ Zeile ab. Wenn Sie nur Leerzeilen (nach POSIX) für leer halten, dann ist das richtig. Aber vielleicht möchten Sie auch Zeilen, die nur aus Leerzeichen bestehen, als leer betrachten?
awk 'NF { COUNT+=1 } END { print COUNT }' file
8
Diesmal ist das Ergebnis anders, da diese spätere Version auch reine Leerzeichen ignoriert, während die ursprüngliche Version nur Leerzeilen ignorierte. Kannst du den Unterschied sehen? Ich lasse Sie das selbst herausfinden. Zögern Sie nicht, den Kommentarbereich zu verwenden, wenn dies nicht klar genug ist!
Schließlich, wenn Sie nur an Datenzeilen interessiert sind und meine spezielle Eingabedatendatei gegeben ist, könnte ich stattdessen Folgendes schreiben:
awk '+$1 { COUNT+=1 } END { print COUNT }' file
7
Es funktioniert aufgrund der AWK-Typkonvertierungsregeln. Das unäre Plus im Muster erzwingt die Bewertung von $1 in einem numerischen Kontext. In meiner Datei enthalten Datensätze im ersten Feld eine Zahl. Nicht-Datensätze (Überschriften, Leerzeilen, Nur-Leerzeichen-Zeilen) enthalten Text oder nichts. Alle von ihnen sind gleich 0, wenn sie in Zahlen umgewandelt werden.
Beachten Sie, dass bei dieser neuesten Lösung auch ein Datensatz für einen Benutzer mit 0 Credits verworfen wird.
B. Verwenden von Arrays in AWK
Arrays sind eine leistungsstarke Funktion von AWK. Alle Arrays in AWK sind assoziative Arrays, sodass sie es ermöglichen, eine beliebige Zeichenfolge mit einem anderen Wert zu verknüpfen. Wenn Sie mit anderen Programmiersprachen vertraut sind, kennen Sie sie vielleicht als Hashes , assoziative Tabellen , Wörterbücher oder Karten .
9. Ein einfaches Beispiel für ein AWK-Array
Stellen wir uns vor, ich möchte das Gesamtguthaben für alle Benutzer wissen. Ich kann einen Eintrag für jeden Benutzer in einem assoziativen Array speichern, und jedes Mal, wenn ich auf einen Datensatz für diesen Benutzer stoße, erhöhe ich den entsprechenden im Array gespeicherten Wert.
awk '+$1 { CREDITS[$3]+=$1 }
END { for (NAME in CREDITS) print NAME, CREDITS[NAME] }' FS=, file
abhishek 17
sonia 129
öle 8
sylvain 109
Ich gebe zu, das ist kein Einzeiler mehr. Hauptsächlich wegen for
Schleife, die verwendet wird, um den Inhalt des Arrays anzuzeigen, nachdem die Datei verarbeitet wurde. Kommen wir also zurück zu kürzeren Beispielen:
10. Identifizieren doppelter Zeilen mit AWK
Arrays können, genau wie andere AWK-Variablen, sowohl in Aktionsblöcken als auch in Mustern verwendet werden. Indem wir davon profitieren, können wir einen Einzeiler schreiben, um nur doppelte Zeilen zu drucken:
awk 'a[$0]++' file
52,01 dec 2018,sonia,team
Der ++
operator ist der Post-Increment-Operator, der von der C-Sprachfamilie geerbt wurde (deren AWK ein stolzes Mitglied ist, dank Brian Kernighan, einer ihrer ursprünglichen Autoren).
Wie der Name schon sagt, erhöht der Post-Increment-Operator eine Variable („add 1“), aber erst nachdem ihr Wert für die Auswertung des Englobing-Ausdrucks übernommen wurde.
In diesem Fall a[$0]
wird ausgewertet, um zu sehen, ob der Datensatz gedruckt wird oder nicht, und sobald die Entscheidung getroffen wurde, wird der Array-Eintrag in jedem Fall inkrementiert.
Wenn also ein Datensatz zum ersten Mal gelesen wird, a[$0]
ist undefiniert und somit für AWK gleich Null. Dieser erste Datensatz wird also nicht auf die Ausgabe geschrieben. Dann wird dieser Eintrag von Null auf Eins geändert.
Das zweite Mal, wenn derselbe Eingabedatensatz gelesen wird, a[$0]
ist jetzt 1. Das ist „wahr“. Die Zeile wird gedruckt. Davor wird jedoch der Array-Eintrag von 1 auf 2 aktualisiert. Und so weiter.
11. Doppelte Zeilen entfernen
Als Folge des vorherigen Einzeilers möchten wir möglicherweise doppelte Zeilen entfernen:
awk '!a[$0]++' file
CREDITS,EXPDATE,USER,GROUPS
99,01 jun 2018,sylvain,team:::admin
52,01 dec 2018,sonia,team
25,01 jan 2019,sonia,team
10,01 jan 2019,sylvain,team:::admin
8,12 jun 2018,öle,team:support
17,05 apr 2019,abhishek,guest
Der einzige Unterschied besteht in der Verwendung des logischen Nicht-Operators (!
), die den Wahrheitswert des Ausdrucks umkehren. Was falsch war, wird wahr, und was wahr war, wird falsch. Das logische Nicht hat absolut keinen Einfluss auf den ++
Post-Inkrement, das genauso funktioniert wie zuvor.
C. Magische Feld- und Datensatztrenner
12. Änderung der Feldtrenner
awk '$1=$1' FS=, OFS=';' file
CREDITS;EXPDATE;USER;GROUPS
99;01 jun 2018;sylvain;team:::admin
52;01 dec 2018;sonia;team
52;01 dec 2018;sonia;team
25;01 jan 2019;sonia;team
10;01 jan 2019;sylvain;team:::admin
8;12 jun 2018;öle;team:support
17;05 apr 2019;abhishek;guest
Dieses Programm setzt den FS
und OFS
Variable, um ein Komma als Trennzeichen für Eingabefelder und ein Semikolon als Trennzeichen für Ausgabefelder zu verwenden. Da AWK den Ausgabedatensatz nicht ändert, solange Sie kein Feld geändert haben, ist der $1=$1
Trick wird verwendet, um AWK zu zwingen, den Rekord zu brechen und ihn mithilfe des Ausgabefeldtrennzeichens neu zusammenzusetzen.
Denken Sie daran, dass hier der Standard-Aktionsblock { print }
ist . Sie könnten das also expliziter umschreiben als:
awk '$1=$1 { print }' FS=, OFS=';' file
CREDITS;EXPDATE;USER;GROUPS
99;01 jun 2018;sylvain;team:::admin
52;01 dec 2018;sonia;team
52;01 dec 2018;sonia;team
25;01 jan 2019;sonia;team
10;01 jan 2019;sylvain;team:::admin
8;12 jun 2018;öle;team:support
17;05 apr 2019;abhishek;guest
Sie haben vielleicht bemerkt, dass beide Beispiele auch leere Zeilen entfernen. Wieso den? Denken Sie an die AWK-Konvertierungsregeln:Eine leere Zeichenfolge ist „falsch“. Alle anderen Strings sind „true“. Der Ausdruck $1=$1
ist eine Affektion, die $1
verändert . Allerdings ist dies auch ein Ausdruck. Und es ergibt den Wert von $1
– was für die leere Zeichenfolge „false“ ist. Wenn Sie wirklich alle Zeilen wollen, müssen Sie stattdessen vielleicht so etwas schreiben:
awk '($1=$1) || 1 { print }' FS=, OFS=';' file
CREDITS;EXPDATE;USER;GROUPS
99;01 jun 2018;sylvain;team:::admin
52;01 dec 2018;sonia;team
52;01 dec 2018;sonia;team
25;01 jan 2019;sonia;team
10;01 jan 2019;sylvain;team:::admin
8;12 jun 2018;öle;team:support
17;05 apr 2019;abhishek;guest
Erinnerst du dich an den &&
Operator? Es war das logische UND. ||
ist das logische ODER. Die Klammer ist hier aufgrund der Vorrangregeln der Operatoren erforderlich. Ohne sie wäre das Muster fälschlicherweise als $1=($1 || 1)
interpretiert worden stattdessen. Ich lasse Sie als Übung testen, wie das Ergebnis dann anders ausgefallen wäre.
Und schließlich, wenn Sie nicht allzu begeistert von Arithmetik sind, wette ich, dass Sie diese einfachere Lösung bevorzugen werden:
awk '{ $1=$1; print }' FS=, OFS=';' file
CREDITS;EXPDATE;USER;GROUPS
99;01 jun 2018;sylvain;team:::admin
52;01 dec 2018;sonia;team
52;01 dec 2018;sonia;team
25;01 jan 2019;sonia;team
10;01 jan 2019;sylvain;team:::admin
8;12 jun 2018;öle;team:support
17;05 apr 2019;abhishek;guest
13. Entfernen mehrerer Leerzeichen
awk '$1=$1' file
CREDITS,EXPDATE,USER,GROUPS
99,01 jun 2018,sylvain,team:::admin
52,01 dec 2018,sonia,team
52,01 dec 2018,sonia,team
25,01 jan 2019,sonia,team
10,01 jan 2019,sylvain,team:::admin
8,12 jun 2018,öle,team:support
17,05 apr 2019,abhishek,guest
Dies ist fast das gleiche Programm wie das vorherige. Die Feldtrennzeichen habe ich jedoch auf ihren Standardwerten belassen. Daher werden mehrere Leerzeichen als Trennzeichen für Eingabefelder verwendet, aber nur ein Leerzeichen wird als Trennzeichen für Ausgabefelder verwendet. Dies hat den netten Nebeneffekt, Vielfache zu verschmelzen Leerzeichen in eins Leerzeichen.
14. Linien verbinden mit AWK
OFS
haben wir bereits verwendet , das Ausgabefeldtrennzeichen. Wie Sie vielleicht erraten haben, hat es den ORS
Gegenstück zur Angabe des Trennzeichens für den Ausgabesatz:
awk '{ print $3 }' FS=, ORS=' ' file; echo
USER sylvain sonia sonia sonia sylvain öle abhishek
Hier habe ich nach jedem Datensatz ein Leerzeichen anstelle eines Zeilenumbruchzeichens verwendet. Dieser Einzeiler ist in einigen Anwendungsfällen ausreichend, hat aber dennoch einige Nachteile.
Ganz offensichtlich verwirft es keine reinen Leerzeichen (die zusätzlichen Leerzeichen nach öle kommen davon). Daher kann es sein, dass ich stattdessen einen einfachen regulären Ausdruck verwende:
awk '/[^[:space:]]/ { print $3 }' FS=, ORS=' ' file; echo
USER sylvain sonia sonia sonia sylvain öle abhishek
Es ist jetzt besser, aber es gibt immer noch ein mögliches Problem. Es wird offensichtlicher, wenn wir das Trennzeichen in etwas sichtbares ändern:
awk '/[^[:space:]]/ { print $3 }' FS=, ORS='+' file; echo
USER+sylvain+sonia+sonia+sonia+sylvain+öle+abhishek+
Am Ende der Zeile befindet sich ein zusätzliches Trennzeichen, da das Feldtrennzeichen hinter geschrieben wird jeder Datensatz. Einschließlich des letzten.
Um das zu beheben, werde ich das Programm umschreiben, um ein benutzerdefiniertes Trennzeichen vorher anzuzeigen den Datensatz, beginnend mit dem zweiten Ausgabedatensatz.
awk '/[^[:space:]]/ { print SEP $3; SEP="+" }' FS=, ORS='' file; echo
USER+sylvain+sonia+sonia+sonia+sylvain+öle+abhishek
Da ich mich selbst um das Hinzufügen des Trennzeichens kümmere, setze ich auch das standardmäßige AWK-Ausgabedatensatztrennzeichen auf den leeren String. Wenn Sie jedoch anfangen, sich mit Trennzeichen oder Formatierungen zu beschäftigen, kann es das Zeichen sein, das Sie über die Verwendung des printf
nachdenken sollten Funktion anstelle von print
Erklärung. Wie wir es gleich sehen werden.
D. Feldformatierung
Die Beziehung zwischen den Programmiersprachen AWK und C habe ich bereits erwähnt. Unter anderem erbt AWK von der Standardbibliothek der C-Sprache das mächtige printf
Funktion, die eine große Kontrolle über die Formatierung des an die Ausgabe gesendeten Textes ermöglicht.
Der printf
Die Funktion nimmt ein Format als erstes Argument, das sowohl reinen Text enthält, der wörtlich ausgegeben wird, als auch Platzhalter, die zum Formatieren verschiedener Abschnitte der Ausgabe verwendet werden. Die Wildcards sind durch den %
gekennzeichnet Charakter. Am gebräuchlichsten ist %s
(für Zeichenkettenformatierung), %d
(für die Formatierung von Ganzzahlen) und %f
(für die Formatierung von Fließkommazahlen). Da dies ziemlich abstrakt sein kann, sehen wir uns ein Beispiel an:
awk '+$1 { printf("%s ", $3) }' FS=, file; echo
sylvain sonia sonia sonia sylvain öle abhishek
Sie können feststellen, dass das Gegenteil von print
ist -Anweisung, die printf
Funktion verwendet nicht den OFS
und ORS
Werte. Wenn Sie also ein Trennzeichen wünschen, müssen Sie es explizit erwähnen, wie ich es getan habe, indem Sie am Ende der Formatzeichenfolge ein Leerzeichen hinzufügen. Dies ist der Preis für die vollständige Kontrolle über die Ausgabe.
Obwohl dies überhaupt kein Formatbezeichner ist, ist dies eine hervorragende Gelegenheit, den \n
vorzustellen Notation, die in jedem AWK-String verwendet werden kann, um ein Zeilenumbruchzeichen darzustellen.
awk '+$1 { printf("%s\n", $3) }' FS=, file
sylvain
sonia
sonia
sonia
sylvain
öle
abhishek
15. Tabellenergebnisse erstellen
AWK erzwingt ein Datensatz-/Felddatenformat basierend auf Trennzeichen. Verwenden Sie jedoch den printf
-Funktion können Sie auch eine tabellarische Ausgabe mit fester Breite erzeugen. Da jeder Formatbezeichner in einem printf
-Anweisung kann einen optionalen Breitenparameter akzeptieren:
awk '+$1 { printf("%10s | %4d\n", $3, $1) }' FS=, file
sylvain | 99
sonia | 52
sonia | 52
sonia | 25
sylvain | 10
öle | 8
abhishek | 17
Wie Sie sehen können, füllt AWK sie durch Angabe der Breite jedes Felds links mit Leerzeichen auf. Bei Text ist es normalerweise vorzuziehen, rechts aufzufüllen, was mit einer negativen Breitenzahl erreicht werden kann. Außerdem möchten wir für ganze Zahlen Felder mit Nullen anstelle von Leerzeichen auffüllen. Dies kann durch eine explizite 0 vor der Feldbreite erreicht werden:
awk '+$1 { printf("%-10s | %04d\n", $3, $1) }' FS=, file
sylvain | 0099
sonia | 0052
sonia | 0052
sonia | 0025
sylvain | 0010
öle | 0008
abhishek | 0017
16. Umgang mit Fließkommazahlen
Der %f
Format verdient nicht viele Erklärungen…
awk '+$1 { SUM+=$1; NUM+=1 } END { printf("AVG=%f",SUM/NUM); }' FS=, file
AVG=37.571429
… außer vielleicht zu sagen, dass Sie fast immer die Feldbreite und Genauigkeit des angezeigten Ergebnisses explizit festlegen möchten:
awk '+$1 { SUM+=$1; NUM+=1 } END { printf("AVG=%6.1f",SUM/NUM); }' FS=, file
AVG= 37.6
Hier beträgt die Feldbreite 6, was bedeutet, dass das Feld den Platz von 6 Zeichen einnimmt (einschließlich des Punktes und schließlich wie üblich links mit Leerzeichen aufgefüllt). Die Genauigkeit .1 bedeutet, dass wir die Zahl mit 1 Dezimalzahl nach dem Punkt anzeigen möchten. Ich lasse Sie raten, was %06.1
ist würde stattdessen angezeigt werden.
E. Zeichenkettenfunktionen in AWK verwenden
Zusätzlich zum printf
-Funktion enthält AWK einige andere nette Funktionen zur Bearbeitung von Zeichenfolgen. In diesem Bereich haben moderne Implementierungen wie Gawk einen reichhaltigeren Satz interner Funktionen zum Preis einer geringeren Portabilität. Ich selbst beschränke mich hier auf einige wenige POSIX-definierte Funktionen, die überall gleich funktionieren sollten.
17. Text in Großbuchstaben umwandeln
Dieses verwende ich oft, weil es Internationalisierungsprobleme gut handhabt:
awk '$3 { print toupper($0); }' file
99,01 JUN 2018,SYLVAIN,TEAM:::ADMIN
52,01 DEC 2018,SONIA,TEAM
52,01 DEC 2018,SONIA,TEAM
25,01 JAN 2019,SONIA,TEAM
10,01 JAN 2019,SYLVAIN,TEAM:::ADMIN
8,12 JUN 2018,ÖLE,TEAM:SUPPORT
17,05 APR 2019,ABHISHEK,GUEST
Tatsächlich ist dies wahrscheinlich die beste und portabelste Lösung, um Text von der Shell aus in Großbuchstaben umzuwandeln.
18. Ändern eines Teils einer Zeichenfolge
Mit substr
Befehl können Sie eine Zeichenfolge mit einer bestimmten Länge aufteilen. Hier verwende ich es, um nur das erste Zeichen des dritten Felds groß zu schreiben:
awk '{ $3 = toupper(substr($3,1,1)) substr($3,2) } $3' FS=, OFS=, file
CREDITS,EXPDATE,USER,GROUPS
99,01 jun 2018,Sylvain,team:::admin
52,01 dec 2018,Sonia,team
52,01 dec 2018,Sonia,team
25,01 jan 2019,Sonia,team
10,01 jan 2019,Sylvain,team:::admin
8,12 jun 2018,Öle,team:support
17,05 apr 2019,Abhishek,guest
Der substr
Die Funktion nimmt die Anfangszeichenfolge, den (1-basierten) Index des ersten zu extrahierenden Zeichens und die Anzahl der zu extrahierenden Zeichen. Wenn das letzte Argument fehlt, substr
nimmt alle restlichen Zeichen des Strings.
Also substr($3,1,1)
wird zum ersten Zeichen von $3
ausgewertet , und substr($3,2)
zu den restlichen.
19. Felder in Unterfelder aufteilen
Das AWK-Record-Field-Datenmodell ist wirklich nett. Manchmal möchten Sie jedoch Felder selbst in mehrere Teile aufteilen, basierend auf einem internen Trennzeichen:
awk '+$1 { split($2, DATE, " "); print $1,$3, DATE[2], DATE[3] }' FS=, OFS=, file
99,sylvain,jun,2018
52,sonia,dec,2018
52,sonia,dec,2018
25,sonia,jan,2019
10,sylvain,jan,2019
8,öle,jun,2018
17,abhishek,apr,2019
Etwas überraschend funktioniert dies sogar, wenn einige meiner Felder durch mehr als ein Leerzeichen getrennt sind. Meistens aus historischen Gründen, wenn das Trennzeichen ein einzelnes Leerzeichen ist, split
wird berücksichtigen, dass „die Elemente durch Leerzeichen getrennt sind“. Und nicht nur von einem. Der FS
Sondervariable folgt der gleichen Konvention.
Im allgemeinen Fall entspricht jedoch eine Zeichenkette einem Zeichen. Wenn Sie also etwas Komplexeres benötigen, müssen Sie daran denken, dass das Feldtrennzeichen ein erweiterter regulärer Ausdruck ist.
Sehen wir uns als Beispiel an, wie das Gruppenfeld gehandhabt wird, das ein mehrwertiges Feld mit einem Doppelpunkt als Trennzeichen zu sein scheint:
awk '+$1 { split($4, GRP, ":"); print $3, GRP[1], GRP[2] }' FS=, file
sylvain team
sonia team
sonia team
sonia team
sylvain team
öle team support
abhishek guest
Während ich erwartet hätte, dass bis zu zwei Gruppen pro Benutzer angezeigt werden, wird für die meisten nur eine angezeigt. Dieses Problem wird durch das mehrfache Vorkommen des Trennzeichens verursacht. Die Lösung lautet also:
awk '+$1 { split($4, GRP, /:+/); print $3, GRP[1], GRP[2] }' FS=, file
sylvain team admin
sonia team
sonia team
sonia team
sylvain team admin
öle team support
abhishek guest
Die Schrägstriche anstelle der Anführungszeichen kennzeichnen das Literal als regulären Ausdruck und nicht als einfache Zeichenfolge, und das Pluszeichen gibt an, dass dieser Ausdruck mit einem oder mehreren Vorkommen des vorherigen Zeichens übereinstimmt. In diesem Fall besteht also jedes Trennzeichen (aus der längsten Folge von) einem oder mehreren aufeinanderfolgenden Doppelpunkten.
20. Suchen und Ersetzen mit AWK-Befehlen
Apropos reguläre Ausdrücke, manchmal möchten Sie eine Ersetzung wie sed s///g
durchführen Befehl, aber nur auf einem Feld. Der gsub
Befehl ist das, was Sie in diesem Fall brauchen:
awk '+$1 { gsub(/ +/, "-", $2); print }' FS=, file
99 01-jun-2018 sylvain team:::admin
52 01-dec-2018 sonia team
52 01-dec-2018 sonia team
25 01-jan-2019 sonia team
10 01-jan-2019 sylvain team:::admin
8 12-jun-2018 öle team:support
17 05-apr-2019 abhishek guest
Der gsub
Die Funktion benötigt einen regulären Ausdruck für die Suche, eine Ersatzzeichenfolge und die Variable, die den zu ändernden Text enthält. Wenn das später fehlt, wird $0 angenommen.
F. Arbeiten mit externen Befehlen in AWK
Ein weiteres großartiges Feature von AWK ist, dass Sie externe Befehle einfach aufrufen können, um Ihre Daten zu verarbeiten. Dafür gibt es grundsätzlich zwei Möglichkeiten:mit dem system
Anweisung, ein Programm aufzurufen und es seine Ausgabe in den AWK-Ausgabestrom mischen zu lassen. Oder verwenden Sie eine Pipe, damit AWK die Ausgabe des externen Programms erfassen kann, um das Ergebnis genauer zu steuern.
Das mag für sich genommen ein riesiges Thema sein, aber hier sind ein paar einfache Beispiele, die Ihnen die Leistungsfähigkeit hinter diesen Funktionen zeigen.
21. Hinzufügen des Datums über einer Datei
awk 'BEGIN { printf("UPDATED: "); system("date") } /^UPDATED:/ { next } 1' file
UPDATED: Thu Feb 15 00:31:03 CET 2018
CREDITS,EXPDATE,USER,GROUPS
99,01 jun 2018,sylvain,team:::admin
52,01 dec 2018,sonia,team
52,01 dec 2018,sonia,team
25,01 jan 2019,sonia,team
10,01 jan 2019,sylvain,team:::admin
8,12 jun 2018,öle,team:support
17,05 apr 2019,abhishek,guest
In diesem AWK-Programm zeige ich zunächst die Arbeit UPDATED an. Dann ruft das Programm das externe date
auf Befehl, der sein Ergebnis direkt nach dem von AWK zu diesem Zeitpunkt erzeugten Text an die Ausgabe sendet.
Der Rest des AWK-Programms entfernt einfach eine Update-Anweisung, die möglicherweise in der Datei vorhanden ist, und gibt alle anderen Zeilen aus (mit der Regel 1
).
Beachten Sie das next
Erklärung. Es wird verwendet, um die Verarbeitung des aktuellen Datensatzes abzubrechen. Dies ist eine Standardmethode zum Ignorieren einiger Datensätze aus der Eingabedatei.
22. Modifying a field externally
For more complex cases, you may need to consider the | getline VARIABLE
idiom of AWK:
awk '+$1 { CMD | getline $5; close(CMD); print }' CMD="uuid -v4" FS=, OFS=, file
99,01 jun 2018,sylvain,team:::admin,5e5a1bb5-8a47-48ee-b373-16dc8975f725
52,01 dec 2018,sonia,team,2b87e9b9-3e75-4888-bdb8-26a9b34facf3
52,01 dec 2018,sonia,team,a5fc22b5-5388-49be-ac7b-78063cbbe652
25,01 jan 2019,sonia,team,3abb0432-65ef-4916-9702-a6095f3fafe4
10,01 jan 2019,sylvain,team:::admin,592e9e80-b86a-4833-9e58-1fe2428aa2a2
8,12 jun 2018,öle,team:support,3290bdef-fd84-4026-a02c-46338afd4243
17,05 apr 2019,abhishek,guest,e213d756-ac7f-4228-818f-1125cba0810f
This will run the command stored in the CMD
variable, read the first line of the output of that command, and store it into the variable $5
.
Pay special attention to the close statement, crucial here as we want AWK to create a new instance of the external command each time it executes the CMD | getline
Erklärung. Without the close statement, AWK would instead try to read several lines of output from the same command instance.
23. Invoking dynamically generated commands
Commands in AWK are just plain strings without anything special. It is the pipe operator that triggers external programs execution. So, if you need, you can dynamically construct arbitrary complex commands by using the AWK string manipulation functions and operators.
awk '+$1 { cmd = sprintf(FMT, $2); cmd | getline $2; close(cmd); print }' FMT='date -I -d "%s"' FS=, file
99 2018-06-01 sylvain team:::admin
52 2018-12-01 sonia team
52 2018-12-01 sonia team
25 2019-01-01 sonia team
10 2019-01-01 sylvain team:::admin
8 2018-06-12 öle team:support
17 2019-04-05 abhishek guest
We have already met the printf
Funktion. sprintf
is very similar but will return the built string rather than sending it to the output.
24. Joining data
To show you the purpose of the close statement, I let you try out that last example:
awk '+$1 { CMD | getline $5; print }' CMD='od -vAn -w4 -t x /dev/urandom' FS=, file
99 01 jun 2018 sylvain team:::admin 1e2a4f52
52 01 dec 2018 sonia team c23d4b65
52 01 dec 2018 sonia team 347489e5
25 01 jan 2019 sonia team ba985e55
10 01 jan 2019 sylvain team:::admin 81e9a01c
8 12 jun 2018 öle team:support 4535ba30
17 05 apr 2019 abhishek guest 80a60ec8
As the opposite of the example using the uuid
command above, there is here only one instance of od
launched while the AWK program is running, and when processing each record, we read one more line of the output of that same process.
Schlussfolgerung
That quick tour of AWK certainly can’t replace a full-fledged course or tutorial on that tool. However, for those of you that weren’t familiar with it, I hope it gave you enough ideas so you can immediately add AWK to your toolbox.
On the other hand, if you were already an AWK aficionado, you might have found here some tricks you can use to be more efficient or simply to impress your friends.
However, I do not pretend been exhaustive. So, in all cases, don’t hesitate to share your favorite AWK one-liner or any other AWK tips using the comment section below!