- GNU
awk durchläuft automatisch stdin und Dateien und erledigt CLI-Textarbeit, indem es Filterung, Ersetzung und Feldverarbeitung in Einzeilern kombiniert
/regexp/ und !/regexp/ sind Kurzsyntax zur Prüfung der aktuellen Eingabezeile; ist die Bedingung wahr, wird standardmäßig $0 ausgegeben
sub ersetzt nur den ersten Treffer, gsub alle Treffer; wird das Ziel ausgelassen, wird die Ersetzung auf die aktuelle Eingabezeile $0 angewendet
- Dank feldweiser Trennung anhand von Leerraum und
$N, NF, $NF eignet sich awk besonders gut für feldbasierte Verarbeitung, etwa zum Auswählen bestimmter Spalten oder zum Anwenden von Bedingungen
- Indem man Fragmente der Form
cond{action} aneinanderreiht und BEGIN{} sowie END{} ergänzt, lassen sich über einfache Filter hinaus kleine Programme mit Strings, Zahlen und assoziativen Arrays schreiben
Wie awk ausgeführt wird
awk kann wie grep oder sed Text filtern und bietet zusätzlich Programmierfunktionen für komplexere Verarbeitung
- Die Eingabe kann über stdin oder aus Dateien kommen; standardmäßig wird sie zeilenweise durchlaufen und dabei werden Bedingungen und Aktionen angewendet
- Auf die gesamte Eingabezeile greift man über die spezielle Variable
$0 zu
- Der präzise Begriff ist Eingabe-Record, in diesem Kapitel wird es aber zeilenbasiert erklärt
Filterung und Standardausgabe
- Reguläre Ausdrücke werden in der Form
/.../ geschrieben; um zu prüfen, ob ein String auf einen regulären Ausdruck passt, verwendet man die Syntax string ~ /regexp/
- Die Gegenbedingung wird mit
string !~ /regexp/ ausgedrückt
- Lässt man den zu prüfenden String weg, ist
$0 das Ziel
- Gibt es nur eine Bedingung und keine Aktion, wird bei wahrer Bedingung automatisch
$0 ausgegeben
awk '/regexp/' ist die Kurzform von awk '$0 ~ /regexp/{print $0}'
awk '!/regexp/' ist die Kurzform von awk '$0 !~ /regexp/{print $0}'
- In den Beispielen werden nur Zeilen mit
at ausgegeben oder nur Zeilen, die kein e enthalten, um den grundlegenden Filterablauf zu zeigen
Idiomatische Ausgabe mit 1
- In Bedingungsausdrücken werden von 0 verschiedene Zahlen und nichtleere Strings als wahr ausgewertet
awk '1' ist ein idiomatischer Ausdruck für eine Bedingung, die für jede Eingabezeile immer wahr ist
- Fehlt eine Aktion, führt eine wahre Bedingung zur Ausgabe von
$0; daher gibt awk '1' die komplette Eingabe unverändert aus
- Das Ergebnis ist identisch zu
awk '{print $0}' oder einfach cat
Ersetzung: sub und gsub
awk stellt für Suche und Ersetzung die Funktionen sub und gsub bereit
sub(/:/, "-") ersetzt in jeder Eingabezeile nur das erste : durch -
gsub(/:/, "-") ersetzt in jeder Eingabezeile alle : durch -
- Beide Funktionen erhalten als erstes Argument den passenden regulären Ausdruck und als zweites den Ersetzungsstring
- Wird kein Eingabestring separat angegeben, ist das Standardziel
$0
- Ist die Ersetzung erfolgreich, wird die Zieleingabe verändert
- Das
1 nach dem Ersetzungsblock wird als Bedingung außerhalb des Blocks interpretiert und bewirkt, dass das veränderte $0 erneut ausgegeben wird
awk '{sub(/:/, "-")} 1' liefert dasselbe Ergebnis wie awk '{sub(/:/, "-"); print $0}'
- Auch bei bloßem
print ist das Standardausgabeziel $0
Wann man grep, sed oder awk wählen sollte
- Für einfache Zeilenfilterung kann das für diesen Zweck optimierte
grep schneller sein als sed oder awk
- Für Ersetzungsaufgaben kann
sed schneller sein als awk
- Die Funktionen der Werkzeuge entsprechen sich nicht immer 1:1
- Um
grep -o mit sed oder awk nachzubauen, sind mehr Schritte nötig
- Rekursive Suche bietet nur
grep
- Eine passende Diskussion dazu gibt es unter unix.stackexchange: When to use grep, sed, awk, perl, etc
Feldbasierte Verarbeitung
awk wird besonders häufig für feldbasierte Verarbeitung verwendet
- Standardmäßig teilt es eine Eingabezeile anhand von Leerraum auf; auf jedes Feld greift man mit
$N zu
$2 ist das zweite Feld
$NF ist das letzte Feld
NF ist die Gesamtzahl der Felder der aktuellen Eingabezeile
- Das Beispiel
table.txt zeigt kompakt spaltenorientierte Verarbeitung
- Ausgabe des zweiten Feldes jeder Zeile
- Ausgabe nur der Zeilen, deren letztes Feld negativ ist
- Ersetzung von
b durch B nur im ersten Feld
- Die Beispieldateien befinden sich im Verzeichnis example_files
Struktur eines Einzeilers
- Ein typischer
awk-Einzeiler hat die folgende Form
awk 'cond1{action1} cond2{action2} ... condN{actionN}'
- Fehlt die Bedingung, wird die Aktion immer ausgeführt
- Fehlt die Aktion, wird bei wahrer Bedingung
$0 ausgegeben
- Innerhalb eines Blocks können mehrere Anweisungen mit Semikolon
; getrennt werden
- Werden mehrere Blöcke aneinandergereiht, lassen sich in den meisten Einzeilern bedingte Aktionen ohne explizites
if ausdrücken
awk '$NF<0' table.txt gibt nur Zeilen aus, deren letztes Feld negativ ist
BEGIN{} wird vor dem Einlesen der Eingabe ausgeführt
END{} wird ausgeführt, nachdem die gesamte Eingabe verarbeitet wurde
- Details zu Operatoren und Bedingungsausdrücken finden sich im gawk manual: Operators und im gawk manual: Truth Values and Conditions
Strings und Zahlen
awk bestimmt den Typ von Werten abhängig vom Kontext, damit sich auf der CLI kurze Lösungen schreiben lassen
- String-Literale werden in doppelte Anführungszeichen gesetzt
- Für Zahlen können Ganzzahlen, Fließkommazahlen und wissenschaftliche Notation verwendet werden
BEGIN{} kann auch verwendet werden, wenn ein awk-Programm ohne externe Eingabe ausgeführt wird
- Variablen können Zahlen und Strings speichern
- Beispiel:
a=5; b=2.5; print a+b
- Werden Strings direkt nebeneinandergestellt, werden sie verkettet
- Nicht initialisierte Variablen verhalten sich im String-Kontext wie ein leerer String und im Zahlenkontext wie
0
- Wird ein String in einem numerischen Ausdruck verwendet, wird er zwangsweise in eine Zahl umgewandelt
- Beginnt der String nicht mit einer gültigen Zahl, wird er als
0 behandelt
- Führende Leerzeichen werden ignoriert
- Wird an eine Zahl ein String angehängt, wird die Zahl in einen String umgewandelt
- Weitere Details stehen im gawk manual: Constant Expressions und im gawk manual: How awk Converts Between Strings and Numbers
Arrays
- Arrays in
awk sind assoziative Arrays und funktionieren als Schlüssel-Wert-Paare
- Schlüssel können Zahlen oder Strings sein, numerische Schlüssel werden intern jedoch in Strings umgewandelt
- Auch mehrdimensionale Arrays sind möglich
- Im Beispiel werden Werte mit String-Schlüsseln wie
student["id"] und student["name"] gespeichert und abgerufen
- Ob ein Schlüssel existiert, wird in der Form
"id" in student geprüft
- Weitere Details stehen im gawk manual: Arrays
Übungen und nächste Schritte
- Dieses Kapitel führt kurz in die grundlegende Kurzsyntax von
awk, in Filterung, Ersetzung, Feldverarbeitung, Typumwandlung und Arrays ein
- Das nächste Kapitel behandelt reguläre Ausdrücke; die hier vorgestellten Funktionen werden dort in den Beispielen weiter verwendet
- Man kann die Syntax einüben, indem man etwa Feldnummern ändert oder andere Eingaben wie negative oder Fließkomma-Felder ausprobiert
- Eine interaktive TUI-App zum Üben lässt sich aus dem Repository AwkExercises installieren; die Nutzung ist in app_guide.md beschrieben
- Alle Übungsaufgaben sind in Exercises.md gesammelt, die Lösungen stehen in Exercise_solutions.md
1 Kommentare
Hacker-News-Kommentare
Ich mag awk und nutze es ziemlich häufig; einer meiner Hauptanwendungsfälle ist, es wie ein sed mit Zustand zu verwenden.
Zum Beispiel, um eine Zeile nur dann zu matchen, wenn sie nach einer bestimmten vorherigen Zeile kommt – nützlich für schnelle, provisorische Linter.
Kürzlich habe ich eine Prüfung gebaut, die in Migrationsdateien nach
CREATE INDEXohneCONCURRENTLYsucht, was bei sehr großen Tabellen problematisch sein kann. SQL-Anweisungen können sich über mehrere Zeilen erstrecken, daher war simples Matching schwierig.awk kann über mehrere Zeilen hinweg Zustände verfolgen, etwa „befinde mich in einer create-Anweisung“ oder „erstelle gerade einen Index“, und ein grob zusammengebautes Skript läuft damit seit etwa einem Jahr zuverlässig.
Wenn man die aktuelle Zeile nur ausgeben will, wenn die vorherige Zeile einem bestimmten Muster entspricht, geht das etwa mit
sed -ne 'x' -e '/PREV/ {x; /CURR/ p; x}'.Beispiel:
echo -e "PREV\nCURR\nCURR\nCURR\nPREV\nRED" | sed -ne 'x' -e '/PREV/ {x; /CURR/ p; x}'gibt nurCURRaus.Das nutzt den Hold Buffer von sed; mit
-nwird die Standardausgabe unterdrückt, und mitpwerden nur die benötigten Zeilen ausgegeben.xtauscht die aktuelle Zeile mit dem Hold Buffer, und im Block/PREV/ { ... }wird erneut getauscht; ausgegeben wird nur, wenn die aktuelle ZeileCURRenthält.Das letzte
xdient dazu, für überlappende Matches wieder zurückzutauschen.Natürlich ist ein awk-Skript sehr wahrscheinlich viel einfacher und direkter, aber mit sed geht es eben auch.
Die Zeit, das zu lernen, wäre vielleicht besser in awk investiert gewesen, aber das Tutorial https://www.grymoire.com/Unix/Sed.html ist so gut, dass ich fast alles, was ich über sed weiß, dort gelernt habe.
Wer diesen Thread liest, dürfte sich auch für „Ask HN: Share a shell script you like“ von vor zwei Wochen interessieren.
Mit 78 Kommentaren war es nicht so lebhaft wie erhofft, ist aber als Referenz brauchbar: https://news.ycombinator.com/item?id=37112991
Auch vom letzten Jahr gibt es einen Beitrag: https://news.ycombinator.com/item?id=32467957 (374 Punkte, 294 Kommentare)
Neben Lisp und Ähnlichem habe ich mir auch ein kleines Interesse an Awk bewahrt und 2022 cppawk gebaut: https://www.kylheku.com/cgit/cppawk/about/
cppawk erweitert Awk um Präprozessor-Funktionen.
Es gibt ein Loop-Makro mit Unterstützung für mehrere Klauseln; durch Kombinieren von Klauseln lassen sich parallele Iteration oder Iteration über kartesische Produkte erzeugen, und eigene Erweiterungen sind ebenfalls möglich.
Mit fünf einfachen Makros kann man eine neue Klausel definieren.
Wenn man Awk verwendet, kann das nützlich sein; es ist in mehreren Manpages dokumentiert und verfügt über Unit-Tests, die unter gawk und mawk laufen.
Vielleicht spricht da nur mein Gefühl als alter Systemadministrator, aber ich sehe nicht recht, welchen Vorteil awk gegenüber dem einfachen Schreiben derselben Dinge in Perl hat.
Ich habe viele furchtbare Shell-Skripte von Junior-Systemadministratoren gesehen und dachte jedes Mal: „Der Textverarbeitungsteil wäre mit Perl viel sauberer gewesen.“
Für mich ist awk eine der wenigen Sprachen, die ich monatelang liegen lassen kann und nach 10 Minuten wieder im Gefühl habe.
Es hat etwas Intuitives und passt natürlich gut zu den üblichen Kommandozeilen-Tools.
In diesem Beitrag geht es allerdings um kurze Einzeiler für provisorische Zwecke, und in solchen Fällen sind sed/awk besser als Perl.
Wenn man Perl bereits kennt, kann man natürlich einfach weiter Perl verwenden, ohne noch ein weiteres Tool zu lernen.
Als Sprache selbst ist es „ganz okay“, aber ausreichend brauchbar.
Mit Perl kann man natürlich alles ebenfalls machen, muss aber den Boilerplate-Code selbst schreiben, den awk gratis übernimmt.
Die Vorteile der Perl-Sprache sind für mich nicht groß genug, um awk aufzugeben, und da ich awk hauptsächlich für Datenverarbeitung und einmalige Dateibearbeitung statt für „Scripting“ nutze, vermisse ich die Tiefe von Perl nicht.
Wenn ich tiefere Funktionen brauche, gehe ich woandershin.
Weil die heutige Generation Perl nicht gelernt hat, scheint sie die Idee der Stream-Verarbeitung mit awk neu zu entdecken.
awk war nach Maßstäben der späten 1970er eine hervorragende Idee und eine echte Innovation, die es wert war, nachgeahmt zu werden.
Später hat Perl das nachgeahmt und übertroffen, ist aber in Vergessenheit geraten; deshalb wirkt es ein wenig peinlich, dass awk nun wiederentdeckt wird.
Ein weniger bekannter Punkt bei gawk ist, dass es üblicherweise mit nützlichen Erweiterungen ausgeliefert wird.
Man bekommt Zugriff auf Funktionen wie
readdir(),ord(),chr(),gettimeofday()undsleep().https://www.gnu.org/software/gawk/manual/html_node/Extension-Samples.html
awk-Einzeiler sind wirklich mächtig
Die schwierige Frage ist, ob es sich lohnt, in komplexere awk-Programmierung zu investieren
Wenn eine Verarbeitungsaufgabe komplizierte Logik erfordert, kann awk sie zwar liefern, aber in der kurzen, schwer verständlichen Art aus den frühen Tagen der Computertechnik
Entscheidet man sich dagegen für eine moderne Alternative, bringt auch das Aufwand mit sich; insbesondere kann sie wie pandas nicht gerade intuitiv sein und zusätzlich Performance-Probleme verursachen
Selbst eine gemeinsame Datei mit persönlichen Hilfsfunktionen fühlt sich nicht wirklich gut unterstützt an, und es scheint auch keine naheliegende Methode zu geben, Third-Party-Bibliotheken einzubinden
Wenn ich Helper-Funktionen brauche und anfange, den Code auf mehrere Zeilen aufzuteilen, wechsle ich normalerweise zu Python
Dann wird das Programm zwei- bis dreimal so groß, und ich seufze
Ruby ist ein hervorragender awk-Ersatz, aber wenn die Kolleginnen und Kollegen Ruby nicht kennen, kann man kaum erwarten, dass sie es warten
Heute kann man solche schwer lesbaren Befehle in eine KI einfügen, und sie erklärt sie Schritt für Schritt ziemlich gut, damit ist es okay geworden
Wegen der Testbarkeit mit Unit-Tests bevorzuge ich trotzdem ein paar Zeilen Python, aber bei schnellen Datenaufbereitungen braucht man manchmal von vornherein keine Unit-Tests
Eine neue Version des E-Books „CLI text processing with GNU awk“ wurde veröffentlicht
Man kann
GNU awkanhand von Hunderten Beispielen und Übungen Schritt für Schritt von Grundlagen bis Fortgeschrittenem lernen; ausführlich behandelt werden Feldverarbeitung, Filtern, Verarbeitung mehrerer Dateien, Lösungen, die von mehreren Records abhängen, Record- und Feldvergleiche zwischen Dateien, Duplikatsuche unter Beibehaltung der Eingabereihenfolge, reguläre Ausdrücke und mehrPDF/EPUB: https://learnbyexample.gumroad.com/l/gnu_awk (kostenlos bis 31. August 2023)
Web-Version: https://learnbyexample.github.io/learn_gnuawk/
Markdown-Quellen und Beispieldateien: https://github.com/learnbyexample/learn_gnuawk
Interaktive TUI-App zum Üben: https://github.com/learnbyexample/TUI-apps/blob/main/AwkExercises
Als Bundle gibt es Magical one-liners, eine Sammlung von grep-, sed-, awk-, perl- und ruby-Einzeilern, für 5 $: https://learnbyexample.gumroad.com/l/oneliners/new_awk_release, sowie ein Bundle aller 13 E-Books für 12 $: https://learnbyexample.gumroad.com/l/all-books/new_awk_release
Feedback ist sehr hilfreich, ganz gleich ob zu Tippfehlern, Codefehlern, gelungenen oder weniger gelungenen Teilen
Frühere Diskussionen gibt es unter https://news.ycombinator.com/item?id=15549318 und https://news.ycombinator.com/item?id=22758217
Es könnte durchaus nützlich genug sein, um 5 oder 15 $ zu zahlen, aber es könnte auch nichts anderes sein als das, was ich ohnehin schon in meiner persönlichen Einzeiler-Datei gespeichert habe, daher zögere ich, nur zum Nachsehen zu bezahlen
Ich frage mich, ob die Web-Version auch nach dem 31. August weiterhin kostenlos bleibt
Ich habe früher einmal Code-Golf für Herzen mit AWK zeichnen gemacht: https://gist.github.com/auselen/906a53b47a7d616b080dbef85eb8f776
In 99,9 % der awk-Nutzung geht es darum, Zeilen aufzuteilen und dabei aufeinanderfolgende Leerzeichen zu verwerfen
Beispiel:
echo "key: value" | awk '{print $1}'Eine einfachere Alternative wäre schön
Es ist in Nim geschrieben, aber das muss keine große Hürde sein, und auch die anderen Tools in
bu/könnten interessant seincutgeht es auch:echo "key: value" | cut -wf 2Ob das tatsächlich einfacher ist, ist allerdings diskutabel
Beim Nachsehen stellte sich heraus, dass GNU cut kein
-what, das ist also BSD-spezifischAm Ende ist es wohl besser, awk zu verwenden
Beide sind cut/awk-Alternativen und unterstützen auch Regex-basierte Trennung, aber führende/abschließende Leerzeichen entfernen sie, soweit ich mich erinnere, nicht
Mein eigenes https://github.com/learnbyexample/regexp-cut ist ein
cut-ähnliches Tool, dasawkverwendet und Regex-basierte Trennung, negative Indizes usw. bietet; dank des Standardverhaltens von awk behandelt es auch führende/abschließende Leerzeichenecho "key: value" | awk '{print $2}'gemeintVor einiger Zeit habe ich ein aus Bash portiertes diff2html-Skript in awk geschrieben, und aus naheliegenden Gründen war es deutlich schneller.
Im Vergleich zum Bash-Skript war die awk-Version viel leichter zu lesen, und ich konnte die Sprache an einem Abend lernen, debuggen, die Bugs verstehen und beheben.
Ich weiß nicht, ob das idiomatisches awk ist, aber es fühlte sich wirklich wie eine ziemlich gute Sprache an.
https://github.com/berry-thawson/diff2html/blob/master/diff2html.sh