awk/awk-ALTESEITE
awk ist eine Sprache zur Verarbeitung und Auswertung von Textdateien und besonders gut geeignet für die Listenbearbeitung. Die Bezeichnung awk setzt sich einfach aus den Anfangsbuchstaben der Nachnamen der Erfinder dieser Sprache zusammen: Aho, Kernighan und Weinberger.
Der awk zählt zu den grundsätzlichen Tools, die auf jedem UNIX-System verfügbar sein sollten. Er wird recht oft von anderen Programmen benutzt und sollte deshalb auf jeden Fall mit installiert werden. Unter Linux wird die GNU Implementierung gawk benutzt, die ausser allen im POSIX-Standard vorgesehenen Features auch die Erweiterungen aus SVR4 unterstützt.
Die Fähigkeiten des Programms:
- Suchen und Ersetzen von Textmustern
- Zugriff auf einzelne Felder
- Bildung von Summen
- Erkennung regulärer Ausdrücke
- Vornahme von "BEGIN"- und "END"-Verarbeitungen
- Zugriff auf die awk-Programmiersprache (beschrieben im Dokument "POSIX 1002.3 Command language and utilities standard")
Aufruf und Optionen
Ein awk-Skript besteht aus einzelnen Befehlen oder Befehlsblöcken, die entweder nur unter bestimmten Bedingungen oder für alle Zeilen der Eingabe ausgeführt werden. awk-Befehle können direkt von der Befehlszeile aufgerufen oder in Dateien gespeichert werden.
Arbeitsweise: awk bearbeitet nacheinander alle Sätze einer Datei, wobei als Satztrennzeichen gewöhnlich das newline-Zeichen angesehen wird.
Jeder Satz wird in Felder zerlegt, wobei der Feldtrenner eine Folge von Tabulatoren und Leerzeichen ist. Andere Feldtrenner können mit der Option "-F" oder "FS" eingestellt werden.
Der Zugriff auf bestimmte Felder eines Satzes erfolgt durch Angabe der Feldnummer mit vorangestelltem Dollarzeichen (z. B. "$3" für das dritte Feld), der gesamte Satz ist über "$0" ansprechbar.
Der awk ist ein Interpreter. Im einfachsten Fall ist das auszuführende Skript so kurz, dass man es gleich mit auf der Befehlszeile angeben kann:
awk [Optionen] [--] Programmtext Datei ...
Folgendes Beispiel gibt Namen der Benutzer und das jeweilige Home-Verzeichnis aus.
$ awk -F: '{print $5, $6}' /etc/passwd
Dabei gibt die Option "-F:" an, dass der Doppelpunkt als Trennzeichen zwischen Feldern aufgefasst werden soll. Das eigentliche awk-Programm besteht aus "{print $1, $6}". Die doppelten Anführungszeichen verstecken die im Skript enthaltenen Sonderzeichen vor der Shell. Die geschweiften Klammern gehören zum Skript!
Im awk werden Anweisungen, ähnlich der Shell, durch Strichpunkte getrennt. Damit kann man auch mehrere Anweisungen auf der Befehlszeile unterbringen. Trotzdem ist die Grenze der Übersichtlichkeit schnell erreicht und man schreibt das awk-Skript lieber in eine Datei. Der Skriptaufruf sieht dann so aus:
awk [Optionen] -f Skriptdatei [--] Datei ...
Die Programmdatei wird dabei entsprechend der Pfadangaben in der Umgebungsvariable "AWKPATH" gesucht. Ist diese Variable nicht gesetzt, so wird das aktuelle Verzeichnis und anschließend "/usr/local/share/awk" durchsucht.
Bei Bedarf kann auch direkt ein awk-Skript geschrieben werden, das als erste Zeile stets den Pfad zum awk-Interpreter enthalten muss.
#!/bin/awk -f BEGIN { "Keine Angst vor awk! }
Grundzüge der Sprache
Programmablauf
Ein awk-Programm besteht aus einer Folge von pattern-action statements:
Suchmuster { action statements }
Ausserdem gibt es die Möglichkeit, Funktionen zu definieren:
function name(Parameterliste) { statements }
awk liest zunächst das komplette Programm ein und überführt es in eine interne Darstellung, die sich schneller ausführen lässt. Dann wird zunächst ein eventuell vorhandener "BEGIN"-Block abgearbeitet. Dieser Block umfasst die Statements, die mit dem speziellen Suchmuster "BEGIN" angegeben wurden. Hier kann man z.B. Variable initialisieren.
Dann werden alle Dateien in der angegebenen Reihenfolge zeilenweise gelesen. Für jede Zeile wird geprüft, mit welchen Suchmustern diese übereinstimmt. Die für diese Suchmuster angegebenen Aktionen werden ausgeführt.
Wenn alle Eingabezeilen verarbeitet sind, werden die Bestimmungen im "END"-Block abgearbeitet. Der "END"-Block ist durch das spezielle Suchmuster "END" gekennzeichnet. Hier werden häufig Summen ausgegeben.
Das folgende Beispiel druckt eine etwas schöner formatierte Liste aus der Datei "/etc/passwd", wobei nur Benutzer aufgeführt werden, die die bash als Login-Shell benutzen:
BEGIN { FS=":"; print "Bash-Benutzer:" print "-------------------------------------------------------------------"; printf "%-15s %-30s %s\n", "user-id", "Name", "Home"; print "-------------------------------------------------------------------"; } /\/bin\/bash/ {printf "%-15s %-30s %s\n", $1, $5, $6}
BEGIN { FS="^\"?|\"?,\"?|\"?$" printf "%7s %s\n", "Preis", "Titel" print "---------------------------------------------------" summe=0 ARGV[1]="datei.csv" ARGC=2 } { printf "%7.2lf %s\n", $3, $2 summe+=$3 } END { print "---------------------------------------------------" printf "%7.2lf\n", summe }
Sätze, Felder und Variable
awk verwendet dynamische Variable. Diese brauchen nicht deklariert zu werden und können Strings oder Gleitkommawerte enthalten.
Sätze entsprechen normalerweise Zeilen, sie werden also durch das "LF" voneinander getrennt. Man kann aber auch durch Setzen der speziellen Variable "RS" einen anderen Satztrenner festlegen. Wird "RS" mit einer leeren Zeichenkette belegt, so werden Sätze durch Leerzeilen getrennt.
Beim Lesen eines Satzes wird dieser von awk in Felder zerlegt. Die Trennung erfolgt normalerweise durch Whitespace (Leerzeichen oder Tabulatoren), kann aber durch Setzen der Variable "FS" anders geregelt werden. Wird "FS" mit einer leeren Zeichenkette belegt, so wird aus jedem einzelnen Zeichen ein Feld.
Enthalten "RS" und "FS" mehr als ein Zeichen, so wird ihr Inhalt als regulärer Ausdruck aufgefasst. Oft findet man auch awk-Aufrufe folgender Gestalt:
awk -v 'FS=^\"?|\"?,\"?|\"?$' '{print $3, $2}' file.csv
Inhalt der Datei "file.csv":
"Harvest",19.90 "American Stars 'n Bars",24.50 "Unplugged",32.90 "Tonight's the night",16.00 "Deja vu",28.90 "Harvest Moon",32.90 "Old Ways",29.50
Hier wird die Zuweisung der Variable "FS" auf der Befehlszeile vorgenommen. Der oben angegebene Ausdruck zerlegt CSV-Dateien, wie sie von Excel oder dBase exportiert werden, in einzelne Felder.
Felder werden im Programm durch das Dollarzeichen mit nachgestellter Position im Eingabesatz bezeichnet. Dabei werden die Positionen mit 1 beginnend gezaehlt. "$7" bezeichnet also das siebte Feld. Mit "$0" erreicht man den gesamten Eingabesatz. Man kann auch Werte an Felder zuweisen. Auch ist es möglich, die Feldposition einer Variable zu entnehmen. Ausgeben des fünften Feldes:
n=5 print $n
Eingebaute Variable
awk verfügt über einige eingebaute Variable. Die wichtigsten davon sind:
ARGC | Anzahl der Befehlszeilenparameter |
ARGV | Array der Befehlszeilenparameter. Die Indizes laufen von 0 bis ARGC -1. Durch ändern von ARGV kann man vom Skript aus weitere Dateien öffnen. |
CONVFMT | Das voreingestellte Format für Zahlen. Standardwert ist "%.6g". |
ENVIRON | Stellt die Umgebungsvariablen als assoziatives Array zur Verfügung. Ausgabe des Home-Verzeichnisses mit ENVIRON["HOME"] |
ERRNO | Text zum letzten aufgetretenen Fehler bei einer Dateioperation. |
FIELDWIDTHS | Wenn man diese Variable mit einer durch Leerzeichen getrennten Liste von Zahlen füllt, so werden die Felder nicht durch die in "FS" angegebenen Trennzeichen, sondern an den entsprechenden festen Positionen getrennt. Es kann z. B. verwendet werden, um vom Host per FTP übertragene Dateien in Felder zu zerlegen und weiterzuverarbeiten. |
FNR | Die Nummer des aktuellen Eingabesatzes. Ein awk '{print FNR, $0}' liefert ein Listing mit Zeilennummern. |
IGNORECASE | Hat diese Variable einen von Null verschiedenen Wert, so werden alle Stringvergleiche, das Trennen der Eingabe mit "FS" bzw. "RS" und die Auswertung regulärer Ausdrücke unabhängig von Gross- bzw. Kleinschreibung vorgenommen. |
NF | Liefert die Anzahl Felder im aktuellen Eingabesatz. |
OFMT | Das Standard-Ausgabeformat für Zahlen. Voreingestellt ist "%.6g". |
OFS | Das Feldtrennzeichen für die Ausgabe. Voreingestellt ist ein Leerzeichen. |
ORS | Das Satztrennzeichen für die Ausgabe. Voreingestellt ist "LF". Braucht man Zeilenenden im DOS-Format (CR+LF), kann man das (unter anderem) mit dem awk erledigen: awk -v 'ORS=\r\n' '{print $0}' |
Es existieren weitere Variablen, auf die hier nicht weiter eingegangen wird.
Typen
awk kennt keine echten Variablentypen, es wird nur mit Strings hantiert. Diese werden allerdings als Zahlen aufgefasst, wenn das im Zusammenhang sinnvoll ist. Soll die Auswertung als Zahl erzwungen werden, so addiert man eine 0.
Neben den einfachen Strings kennt awk noch assoziative Arrays. Die Indizes eines solchen Arrays sind (im Gegensatz zu C) Strings. Man kann auch mehrdimensionale Arrays verwenden.
Aktionen
Die Aktionen werden in geschweifte Klammern eingeschlossen. Aktionen können Zuweisungen, Verzweigungen und Schleifen enthalten. Die Syntax ist dabei ähnlich C.
Suchmuster und Aktionen
Die Anweisungen des awk bestehen aus dem Suchmuster und den in geschweifte Klammern eingeschlossenen Aktionen. Fehlt das Suchmuster, so werden die Aktionen für alle Eingabezeilen durchgeführt. Fehlt die Aktion, so wird die Eingabezeile unverändert ausgegeben. Der folgende "awk"-Aufruf entspricht einem einfachen grep:
$ awk '/reg. Ausdruck/' Datei
Das Script kann Kommentare enthalten. Diese beginnen mit dem Zeichen "#" und erstrecken sich bis zum Zeilenende. Leerzeilen sind zulässig. Ein Statement endet normalerweise am Zeilenende. Um Statements auf der nächsten Zeile fortzusetzen, maskiert man das Zeilenende mit einem umgekehrten Schrägstrich.
Suchmuster
Folgende Suchmuster sind möglich:
BEGIN | Aktion vor Einlesen des ersten Satzes. |
END | Aktion nach Einlesen des letzten Satzes. |
/rA/ | Sätze, die den erweiterten regulären Ausdruck "rA" enthalten. |
A op rA A op A |
Sätze für die der Vergleichsausdruck wahr ist. |
Suchmuster && Suchmuster | |
Suchmuster || Suchmuster | |
Suchmuster ? Suchmuster : Suchmuster | |
(Suchmuster) | |
! Suchmuster | |
Suchmuster, Suchmuster |
"BEGIN" und "END" wurden schon erläutert. Die regulären Ausdrücke entsprechen denen von egrep. Vergleichende Ausdrücke können umfangreicher sein. Man kann z.B. testen, ob bestimmte Felder einem regulären Ausdruck entsprechen. Die Operatoren "&&", "||", "!" und "?" werden in Analogie zu C angewandt (logisches Und, Oder, Negation und wahlweise Ausführung). Durch Klammern kann man die Reihenfolge der Ausführung bestimmen. Zwei durch Komma getrennte Suchmuster ermitteln einen Bereich von Zeilen, wobei die erste vom ersten, die letzte vom zweiten Suchmuster bestimmt wird.
Operatoren
(...) | Gruppierung |
$ | Feldreferenz |
= | Variablenzuweisung |
++ -- | Inkrement und Dekrement, in postfix- oder präfix-Notation |
^ | Potenzieren |
+ - ! | unäres plus, minus und logische Negation |
+ - * / % | Mathematische Operatoren (Addition, Subtraktion, Multiplikation, Division, Modulo-Funktion) |
space | Stringverkettung |
< > <= >= == != | Vergleichsoperatoren |
~ !~ | match-Operatoren: Vergleich mit regulären Ausdrücken |
in | Test auf Enthaltensein in einem Array |
! && || | Logische Operatoren (Nicht, Und, Oder) |
?: | Bedingter Ausdruck, wie in C |
= += -= *= /= %= ^= | Zuweisung, wie in C |
Verfügbare Variablen
NR | Nummer des aktuellen Satzes. |
NF | Anzahl der Felder des aktuellen Satzes. |
FS | Eingabe-Feldtrennzeichen. |
RS | Eingabe-Satztrennzeichen. |
$i, 1 <= i <= 99 | i-tes Feld des aktuellen Satzes. |
$0 | Der gesamte aktuelle Satz. |
$1, $2, $3 | Erstes, zweites, drittes Feld des aktuellen Satzes. |
length | Länge des Satzes. |
OFS | Ausgabe-Feldtrennzeichen. |
ORS | Ausgabe-Satztrennzeichen. |
FILENAME | Name der gerade bearbeiteten Datei. |
Steueranweisungen
Die meisten Steueranweisungen haben eine direkte Entsprechung in C:
- if (Bedingung) Anweisung [ else Anweisung ]
- while (Bedingung) Anweisung
- do Anweisung while (Bedingung)
- for (Ausdruck1; Ausdruck2; Ausdruck3) Anweisung
- for (Variable in Array) Anweisung
- break
- continue
- delete Array[Index]
- delete Array
- exit [ Ausdruck ]
- { Anweisungen }
Ein/Ausgabe-Anweisungen
close(Datei) | Datei (oder pipe) schliessen |
getline | nächste Zeile in $0 laden |
getline < file | Nächste Zeile aus Datei "file" lesen. |
getline Variable | Nächste Zeile in Variable, statt $0, laden |
getline Variable < file | Zeile aus Datei "file" in Variable laden |
next | Nächste Zeile lesen und ab Anfang des Scriptes bearbeiten |
nextfile | Aktuelle Datei schliessen und mit nächster fortfahren. |
Gibt den aktuellen Satz aus | |
print Ausdrucksliste | Gibt Ergebnis der Ausdrücke aus. |
print Ausdrucksliste > file | Schreibt Ergebnis der Ausdrücke in Datei "file" |
printf Format, Ausdrucksliste | Gibt Ergebnisse formatiert aus |
printf Format, Ausdrucksliste > file | formatierte Ausgabe in Datei "file" |
system(Befehlszeile) | Führt einen Befehl aus |
fflush([Datei]) | Erzwingt das Schreiben der Puffer |
Andere Dateiumleitungen als ">" sind ebenfalls erlaubt. ">>" hängt Daten an eine bestehende Datei an, "|" schreibt die Daten in eine Pipe.
Das Format für printf stimmt im wesentlichen mit dem für die entsprechende C-Funktion überein.
Stringfunktionen
Hier werden nur einige der Stringfunktionen erläutert, genaueres erfährt man in der Manual Page.
gensub(r, s, h [, t]) | Ersetzt einige oder alle Vorkommen eines einem regulären Ausdruck entsprechenden Teilstrings (entspricht substitute im sed bzw. vi). |
gsub(r, s [, t]) | Macht dasselbe wie "gensub", nimmt die Ersetzung aber direkt im entsprechenden Feld vor und liefert die Anzahl vorgenommener Ersetzungen. |
index(s, t) | Liefert die Position der Zeichenkette s in t, bzw. 0, wenn s nicht in t enthalten ist. |
length([s]) | Liefert die Länge von s. Ohne Angabe von s erhält man die Länge des Eingabesatzes $0 |
match(s,r) | Liefert die Position in s, ab der der reguläre Ausdruck r passt, anderenfalls 0. Diese Funktion wird häufig dazu genommen, in einem Suchmuster zu bestimmen, ob ein Feld einem regulären Ausdruck entspricht. Beispiel: $ awk -F: 'match($7,"/bin/bash")' /etc/passwd |
split(s,a,[, r]) | Zerlegt den String "s" in ein Array "a" und liefert die Anzahl Teilstrings. Die Zerlegung erfolgt an Hand des regulären Ausdrucks "r". Falls dieser weggelassen wird, verwendet awk den Inhalt der Variable "FS". |
sprintf(Format, Ausdrucksliste) | Liefert formatierte Ergebnisse der Ausdrücke als String. |
sub(r, s [, t]) | wie "gsub", nimmt aber nur die erste Ersetzung vor. |
substr(s, i [, n]) | Liefert den "n" Zeichen langen Teilstring von "s" ab Position "i". Wird "n" weggelassen, so erhält man den Rest von "s" ab Position "i". |
Verwendung
Ausgabe aller Zeilen der Datei "file".
$ awk '{ print }' file
Ausgabe aller Zeilen der Datei "file" wobei die wichtigsten awk-Variablen in die Datei "awkvars.out" geschrieben werden.
$ awk --dump-variables '{ print }' file
Ausgabe der laufenden Nummer (NR) sowie der Anzahl der Felder (NF) für jede Zeile der Datei "file".
$ awk '{ print NR, NF }' file
Ausgabe jeder Zeile mit vorangestellter Satznummer.
$ awk '{ print NR, $0 }' file
Ausgabe der ersten Spalte aller Sätze von Datei "file".
$ awk '{ print $1 }' file
awk erhält den Inhalt der Variable "$USER" als Befehlsparameter. Der erste übergebene Parameter ("$1") wird mit etwas Text ausgegeben, wobei jeweils ein "|"-Zeichen als Trenner eingesetzt wird.
$ echo $USER | awk '{ print "Hallo " $1 "!" }'
awk erhält den Text "A B C D" und gibt davon nur das dritte und vierte Feld aus, getrennt durch ein Leerzeichen (" "):
$ echo "A B C D" | awk '{ print $3 " " $4 }'
Die Ausgabe von cal (für den Monat Märzt 2007) wird von awk mit der Zeilennummer ("NR") und der Anzahl Felder ("NF") ergänzt und zeilenweise ausgegeben ("$0").
$ cal 03 2007 | awk '{ print NR " | " NF " | " $0 }'
Finden aller Zeilen der Datei "file", die die Zeichenkette "Asia" enthalten und Ausgabe über awk mit Satznummer sowie Ausgabe der Felder1, 2 und 3.
$ egrep 'Asia' file | awk '{ print NR, $1, $2, $3 }'
Falls in Feld 4 die Zeichenkette "Asia" steht, werden Feld 1 und 2 der Datei "file" ausgegeben.
$ awk '$4 == "Asia" { print $1, $2 }' file
Dieselbe Aktion ist in Hochkommas eingeschlossen, um Sonderzeichen wie $ vor einer Auswertung durch die Shell zu schützen. Alternativ könnte auch jedes Sonderzeichen durch den Gegenschrägstrich entwertet werden.
$ awk \$4\ \=\=\ \"Asia\"\ \{\ print\ \$1,\ \$2\ \} file
Wird in der Befehlszeile keine Datei angegeben, so erwartet awk seine Eingabedaten von der Standard-Eingabe. Durch Einsatz des Bindestrichs kann aber auch explizit angegeben werden, dass awk von der Standard-Eingabe lesen soll.
Als Eingabe wird zunächst die Datei "file" und danach die Standard-Eingabe benutzt.
$ awk '{ print $3, $4 }' file -
Aufruf von awk wobei die Anweisungen in der Datei "script.awk" enthalten sind und auf die Datei "file" angewendet werden.
$ awk -f script.awk file
Ausgabe aller Zeilen aus Datei "file", in denen Feld 3 einen Wert höher als 500 aufweist.
$ awk '$3 > 500 { print }' file
Alphanumerische Vergleiche.
$ awk '$1 >= "S" { print }' file
$ awk '$1 >= "s" { print }' file
$ awk '$1 == $4 { print }' file
Ausgabe der dritten Spalte aller Sätze von Datei "file", in denen das "Suchmuster" vorkommt.
$ awk '/Suchmuster/ { print $3 }' file
Beispiel: Ausgabe der Benutzer, die die Bash benutzen:
$ awk '/bash/ { print $0 }' /etc/passwd
Ausgabe aller Zeilen, welche die Zeichenkette "US" enthalten.
$ awk '/US/ { print }' file
Ausgabe des ersten Felds aller Zeilen, in den Feld 4 die Zeichenkette "As" enthält.
$ awk '$4 ~ /As/ { print $1 }' file
Ausgabe der dritten Spalte aller Sätze von Datei "file", in denen ein "P" und oder ein "g" vorkommt.
$ awk '/[Pg]/ { print $3 }' file
Ausgabe der dritten Spalte aller Sätze von Datei "file", deren zweite Spalte ein "0" vorkommt.
$ awk '$2 ~ /0/ { print $3 }' file
Ausgabe aller Spalten der Datei "file", wobei das Suchmuster "pattern1" durch die Zeichenfolge "pattern2" ersetzt wird.
$ awk '{ gsub(/pattern1/,"pattern2"); print $0 }' file
Ausgabe des Inhalts der Datei "file", wobei jede Zeile durchnumeriert wird.
$ awk '{ print NR, $0 }' file
Ausgabe der Anzahl der in der Datei "file" enthaltenen Sätze.
$ awk 'END { N = FILENAME ; print N, "hat", NR, "Sätze." }' file
Ausgabe aller Sätze der Datei "file" mit vorangestellter Satznummer.
$ awk '{ print $1, $2 * $4 }' file
Ausgabe der Spalten 1, 2 und 3 aller Sätze der Datei "file", wobei als Ausgabe-Feldtrennzeichen der Doppelpunkt verwendet wird.
$ awk 'BEGIN { OFS = ":" } { print $1, $2, $3 }' file
Ausgabe der Spalten 2 und 6 (getrennt durch ein Leerzeichen) der Ausgabe des Befehls date:
$ date | awk '{ print $2 " " $6 }'
Ausgabe der ersten Spalte aller Sätze von Datei "file", bei deren dritte Spalte den Wert "0" enthält.
$ awk '$3 == 0 { print $1 }' file
Ausgabe des ersten Feldes (der Gruppenname) der durch Doppelpunkte getrennten Felder der Datei "/etc/group".
$ awk -F':' '{ print $1 }' /etc/group
Ausgabe der mittels Kommas, Tabulatoren oder Leerzeichen getrennten Felder der Datei "file".
$ awk -F'[,[:blank:]]' '{ print $1 $2 $3 }' file
Ausgabe der ersten (Loginname) und dritten (Gcos-Feld) Spalte aus der Datei "/etc/passwd". Die Zeilen werden nur ausgegeben, wenn die numerische UID "0" ist.
$ nawk -F: '$3 == 0 { print $1, $5 }' /etc/passwd
Ausgabe des dritten und vierten Feldes der durch Doppelpunkte getrennten Felder der Datei "/etc/group". print liefert dabei eine unformatierte Ausgabe.
$ awk -F':' '{ print $3 $4 }' /etc/group
Ausgabe des ersten Feldes (der Gruppenname) der durch Doppelpunkte getrennten Felder 3 (Gruppen-ID) und 4 (Benutzer) der Datei "/etc/group". Dier Formatierung der Ausgabe findet dabei mit dem Befehl "printf()" statt, der wie in der Programmiersprache C funktioniert.
$ awk -F':' '{ printf("%5s %s\n", $3, $4 }' /etc/group
Ausgabe aller Felder der durch Doppelpunkte getrennten Felder der Datei "/etc/group", wobei das zweite Feld nicht ausgegeben werden soll und ihm deshalb eine leere Zeichenkette zugewiesen wird.
$ awk -F':' '{ $2=""; print }' /etc/group
Ausgabe der zweiten Spalte aller Sätze von Datei "file", deren Wert höher als "6" ist.
$ awk '$2 > 6' file
Prüfung jeder Zeile der Ausgabe von "ps -ax", ob die Prozessnummer ("$1") unter 100 liegt. Trifft dies zu, werden die Prozessnummer, ein Leerzeichen und der Befehlsname ("$5") ausgedruckt.
$ ps -ax | awk '$1 > 100 { print $1 " " $5 }'
Prüfung jeder Zeile der Ausgabe von "ps -ax", ob die Prozessnummer ("$1") zwischen 50 und 60 liegt. Trifft dies zu, werden die Prozessnummer, ein Leerzeichen und der Befehlsname ("$5") ausgedruckt.
$ ps -ax | awk '$1 > 50 && $1 < 60 { print $1 " " $5 }'
Anzeige der für die Schnittstelle "eth0" eingetragenen IP-Adresse.
$ /sbin/ifconfig eth0 | /usr/bin/awk ' /inet [aA]dd?r/ {print substr ($2, index ($2, ":") + 1) }'
Ausgabe der Länge der längsten Zeile von Datei "file":
$ awk '{ if (length($0) > max) max = length($0) } END { print max }' file
Ausgabe der ersten Spalte aller Zeilen von Datei "file".
$ awk 'BEGIN { print "Wir zählen Zeilen!"; anzahl=0 } { print $1; anzahl++} END {print "Das waren " anzahl " Zeilen." }' file
Skript "pstat.awk" zur Auswertung des Aufrufs "ps -aux | awk -f pstat.awk". Jede Eingabezeile wird untersucht, ob es sich um eine Kopfzeile handelt ( $1 != "USER" ). Trifft dies nicht zu, wird auf die Variablen "cpu" und "mem" das betreffende Feldelement ("$3, $4") addiert und die Anzahl verarbeiteter Zeilen inkrementiert.
$ BEGIN { print "Systemstatus:" } { if ($1 != "USER") { cpu += $3 mem += $4 procs++ } } END { print cpu "% CPU " mem "% MEM " procs " PROCS" }
Das folgende Beispiel bildet Summen nach Spalten und gibt diese und ein Gesamttotal aus:
$ cat > sum 111 222 333 444 555 666 777 888 999 $ cat > sum.awk BEGIN { n = 3 } { for (i=1;i<=n;i++) sum[i] += $i } END { for (i=1;i<=n;i++) { printf "%6g ",sum[i] total += sum[i] } printf "; Total = %6g\n",total } CTRL D $ nawk -f sum.awk sum
1332 1665 1998 ; Total = 4995
Assoziative arrays
Assoziative arrays sind arrays, deren Index ein String ist (unter Perl bekannt als "hash"). Im folgenden Beispiel soll die Datei "sum2" so verarbeitet werden, dass pro Name eine Zeile mit einem Total ausgegeben wird. Die Anweisungen in der Datei "sum2.awk" tun genau das mittels eines assoziativen arrays.
$ cat > sum2 Erich 400 Rita 200 Paul 100 Maria 400 Rita 300 Erich 200 Paul 500 Rita 400 $ cat > sum2.awk { sum [$1] += $2 } END { for (name in sum) print name , sum[name] } $ nawk -f sum2.awk sum2 Maria 400 Erich 600 Rita 900 Paul 600
Einlesen eines beliebigen Textes (im Beispiel die Datei "/etc/passwd"), Anlegen einer Liste mit Worten (Index) und Ablegen der Anzahl (value) in einem assoziativen array. Anschliessend wird eine Liste mit dem Wort und seiner Häufigkeit ausgegeben:
$ cat > zaehl.awk { for (i=1;i<=NF;i++) num[$i]++ } END { for (word in num) print word, num[word] } $ nawk -f zaehl.awk /etc/passwd lp:x:71:8:Line 1 Admin:/usr/spool/lp: 1 user:/usr/kurs4:/usr/bin/ksh 1 ...
Sortieren:
$ nawk -f zaehl.awk absolute | sort -nr +1 I 10 the 8 of 5 have 3 and 3 ...
Sortieren, wobei der Feldtrenner ";" nicht beachtet wird (kann auch direkt ins Skript geschrieben werden:
BEGIN { IFS=; } ):
$ nawk -F: -f zaehl.awk /etc/passwd No Access User 1 2 2 13 nobody 1 3 2 4 3 root 1 ...
Beispiele
Auftrennung der einzelnen Zeilen der Datei "/etc/passwd".
$ cat /etc/passwd | awk -F : '{ print $1" "$2" "$3" "$4}' root x 0 0 bin x 1 1 ...
Im aktuellen Verzeichnis wird jedes Verzeichnis als "Verzeichnis: <Verzeichnisname>" und jede Datei als "Datei: <Dateiname>" ausgegeben.
$ ls -l | awk ' /^d/ { print "Verzeichnis: " $8 } /^-/ { print "Datei: " $8 }'
Im aktuellen Verzeichnis wird jedes Verzeichnis als "<Verzeichnisname>: Verzeichnis" und jede Datei als "<Dateiname>: Datei" ausgegeben.
$ ls -l | awk ' /^d/ { printf("%-15s%-15s\n", $8, ": Verzeichnis") } /^-/ { printf ("%-15s%-15s\n", $8, ": Datei") }'
Anzeige jeder Zeile der Datei "ascii.c", die länger als 30 Zeichen ist.
$ awk 'length($0) > 30' /etc/passwd
Anzeige jeder Zeile der Datei "file", die mindestens ein Feld aufweist (in diesem Fall ein Zeichen), d. h. durch Umleitung der Ausgabe in die Datei "noemptylines" können rasch alle Leerzeilen gelöscht werden.
awk 'NF > 0' file > noemptylines
Berechnung der Dateigrösse in Byte.
$ ls -l /etc/passwd | awk '{ x += $5 } END { print "Byte: " x }' Byte: 1232
Ein kleines C-Programm, das auf der Konsole den ASCII-Code von 33-127 ausdruckt (7 Bit). Zuerst wird die Textdatei "ascii.c" geschrieben, danach mit gcc kompiliert. Die ausführbare Datei heisst dann "asc".
$ vi ascii.c #include <stdio.h> #include <stdlib.h> void get_ascii(void) { int i; int j = 33; int k = 40; int step = (k-j) + 1; for (i = 0; i < 12; ++i) { for ( ; j <= k; ++j) { if (j == 128) { break; } printf("%4d= %c",j,j); } j = (k + 1); k += step; printf("\n\n"); } } int main(void) { printf("\n"); get_ascii(); printf("\n"); } $ gcc -o asc ascii.c $ ./asc 33= ! 34= " 35= # 36= $ 37= % 38= & 39= ' 40= ( 41= ) 42= * 43= + 44= , 45= - 46= . 47= / 48= 0 49= 1 50= 2 51= 3 52= 4 53= 5 54= 6 55= 7 56= 8 57= 9 58= : 59= ; 60= < 61= = 62= > 63= ? 64= @ 65= A 66= B 67= C 68= D 69= E 70= F 71= G 72= H 73= I 74= J 75= K 76= L 77= M 78= N 79= O 80= P 81= Q 82= R 83= S 84= T 85= U 86= V 87= W 88= X 89= Y 90= Z 91= [ 92= \ 93= ] 94= ^ 95= _ 96= ` 97= a 98= b 99= c 100= d 101= e 102= f 103= g 104= h 105= i 106= j 107= k 108= l 109= m 110= n 111= o 112= p 113= q 114= r 115= s 116= t 117= u 118= v 119= w 120= x 121= y 122= z 123= { 124= | 125= } 126= ~ 127=
Abschiessen aller Prozesse, welche die Zeichenfolge "Z39O" enthalten:
for i in $(ps -ef | grep Z39O | awk '{ print $2 }'); do kill -15 $i; done
Wenn die Zeilenzahl grösser als 4 ist, ausserdem die Spalte 12 grösser als 1 und die Spalte 14 grösser als 0 und Spalte 7 das Wort "Afrika" enthält, so werden die Zeilen ausgegeben.
awk 'NR > 4 { if ($12>1) { if ($14>0) { if ($7=="Afrika") { print $12, $14 } } } }' afrika.csv
Alternativ dazu kann der ganze Befehl auch in einer Zeile geschrieben werden:
awk 'NR>4&&$12>1&&$14>0&&$7=="Afrika"{print $12, $14}' afrika.csv
Beispiele 2
Beispieldatei "file":
$ more file Tom 6.20 22 Dick 4.45 5 Harry 6.15 12 Jim 4.85 0 Jake 5.20 0
Ausgabe des Texts "hallo, hallo".
$ awk 'BEGIN { printf("hallo, hallo\n") }' hallo, hallo
Ausgabe aller Zeilen der Datei "file", die in der zweiten Spalte ("$2") einen Wert kleiner als 5 aufweisen. Ausgegeben werden rechtsbündig die ersten zehn Zeichen ("%10s") der ersten Spalte ("$1"), begrenzt durch Doppelpunkte.
$ awk '$2 < 5 { printf(":%10s:\n", $1) }' file : Dick: : Jim:
Ausgabe aller Zeilen der Datei "file", die in der zweiten Spalte ("$2") einen Wert kleiner als 5 aufweisen. Ausgegeben werden rechtsbündig die ersten zehn Zeichen ("%-10s") der ersten Spalte ("$1"), begrenzt durch Doppelpunkte.
$ awk '$2 < 5 { printf(":%-10s:\n", $1) }' file :Dick : :Jim :
Ausgabe aller Zeilen der Datei "file", die in der zweiten Spalte ("$2") einen Wert grösser als 6 aufweisen. Ausgegeben werden die Werte der zweiten Spalte als Fliesskommazahlen auf zwei Stellen genau. (ACHTUNG: Damit die Fliesskommazahlen richtig berechnet werden muss die Variable LANG richtig belegt sein!)
$ awk '$2 > 6 { printf("%6.2f\n", $2) }' file 6.20 6.15
Erstellung einer Gehaltsliste basierend auf den Werten der Datei "file". Falls die dritte Spalte nicht den Wert "0" aufweist ("$3 != 0"), so wird erst linksbündig die erste Spalte und danach rechtsbündig eine Fliesskommazahl auf zwei Stellen genau ausgegeben, die sich aus dem multiplizierten Wert der Spalten zwei und drei ("$2 * $3") ergibt.
$ awk 'BEGIN { print "Gehaltsliste" } $3 != 0 { printf("%-7s%7.2f\n", $1, $2 * $3) }' file Gehaltsliste Tom 136.40 Dick 22.25 Harry 73.80
Berechnung des Durchschnittslohns ausgehend von den Werten in Spalte 2 ("$2") der Datei "file".
$ awk 'BEGIN { printf("Durchschnittslohn:") } > { sum = sum + $2 > i = i + 1 > } > END { if ( i != 0 ) { > printf("%6.2f\n", sum / i) > } > }' file Durchschnittslohn: 5.37
While-Schleife.
$ awk 'END { i =1 > while ( i <= 5 ) { > printf("%2d", i) > i = i + 1 > } > printf("\n") }' file 1 2 3 4 5
For-Schleife. Hier wird zuerst der Initialisierungswert gesetzt ("i = 1"), dann die Bedingung für den Schleifenabbruch ("i <= 5") und schliesslich der Schleifenzähler ("i = i + 1").
$ awk 'END { for( i = 1; i <= 5; i = i + 1) { > printf("%2d", i) > } > printf("\n") }' file 1 2 3 4 5
awk besitzt folgende Funktionen zur Bearbeitung von Zeichenketten:
- length("string") - Länge der Zeichenkette "string")
- index("string1","string2") - Position von "string1" in "string2"
- substr("string",p[,1]) - Aus "string" werden ab Position "p" "1" Zeichen oder alle herausgenommen
Prüfung der Zeilenlänge mit Ausgabe der Nummer der Zeilen, die länger als die angegebene Anzahl Zeichen sind.
$ awk '{ if (length >= 10) { > printf("Zeile %d zu lang\n", NR) > } > }' file
Anzeige in an welcher Position sich in Spalte 1 ("$1") in jeder Zeile der Datei "file" das Zeichen "a" befindet.
$ awk '{ printf("a an Stelle %d\n", index($1,"a")) }' file a an Stelle 0 a an Stelle 0 a an Stelle 2 a an Stelle 0 a an Stelle 2
Anzeige der Buchstaben 1 bis 3 ("1,3") der ersten Spalte ("$1") in jeder Zeile der Datei "file".
$ awk '{ printf("Erste drei Buchstaben: %s\n", substr($1,1,1)) }' file Anfangsbuchstabe T Anfangsbuchstabe D Anfangsbuchstabe H Anfangsbuchstabe J Anfangsbuchstabe J
awk kann auch Vektoren verarbeiten. Die Vektorelemente werden mit "Vektorname[index]" angesprochen.
Anzeige des zweiten Feldes ("zeile[2]") jeder Zeile der Datei "file".
$ awk '{ for( i = NF; i > 0; i = i - 1 ) { > zeile[i] = $i > } > printf("Zweites Feld: %s\n", zeile[2]) > }' file Zweites Feld: 6.20 Zweites Feld: 4.45 Zweites Feld: 6.15 Zweites Feld: 4.85 Zweites Feld: 5.20
Mit der Funktion "split" kann eine beliebige Zeichenfolge in einem Vektor abgespeichert werden:
- split("string",Vektor[,Trenner]) teilt die Zeichenkette "string" an den durch "Trenner" gekennzeichneten Stellen auf und legt die einzelnen Elemente in "Vektor" ab. Standardtrennzeichen ist FS. Der Rückgabewert von "split" ist die Anzahl der Vektorelemente.
$ awk 'BEGIN { Z = "eins:zwei:drei" } > END { n = split(Z, zahl, ":") > for (i = 1; i <= n; i = i + 1) { > printf("Im Feld %d steht %s\n", i, zahl[i]) > } > }' file
Die Indizes für die Vektorelemente können beliebige Zeichenketten sein. Werden als Indizes keine Zahlen gewählt, so können solche Vektoren mit einer besonderen for-Schleife durchlaufen werden:
$ awk ' { for(i = 1; i <= length; i = i + 1) { > z=substr($0, i, 1) > zeichen[z] = zeichen[z] + 1 > } > } > END { for ( z in zeichen ) { > printf("%s kommt %2d-mal vor\n", z, zeichen[z]) > } > }' file 4 kommt 3-mal vor 5 kommt 5-mal vor i kommt 2-mal vor ...