gawk/Programmstart

Aus Foxwiki


Kurze Awk-Programme

  • Die Möglichkeiten zum Aufruf von Awk sind vielfältig.

Für kurze und einmalig verwendete Programme bietet es sich an, diese unmittelbar auf der Kommandozeile anzugeben:

awk 'Programm' <Datei> [<Datei>]
  • Das eigentliche Awk-Programm muss vor der Auswertung durch die Shell geschützt werden, deshalb die Einbettung in einfache Hochkommata.
  • Alle folgenden Elemente der Kommandozeile werden von awk als Dateinamen interpretiert.
  • Fehlt ein solcher Name, erwartet Awk die Daten von der Standardeingabe.
  • Mit Hilfe dieses Schemas lassen sich auf einfache Art und Weise Felder aus den Zeilen der Eingabe extrahieren.

Zur Demonstration lassen wir das erste Feld der Passwortdatei ausgeben, wobei die Zeilen nummeriert werden (der Feldseparator ist der Doppelpunkt und ist durch die eingebaute Variable FS festgelegt):

$ awk 'BEGIN {FS = ":"} {print NR,$1}' /etc/passwd
1 root
2 bin
3 daemon
4 lp
5 news
...

Möchte man ein awk-Programm auf die Ausgabe eines Kommandos anwenden, lässt sich dies über eine Pipe realisieren:

Kommando | awk 'Programm'

Zur Demonstration »verschönert« nachfolgende Kommandofolge die Ausgabe von date :

$ date | awk '{print "Der " $2,$3".", "des Jahres", $6}'
Der 26. Jun. des Jahres 2013

Umfangreiche Awk-Programme

  • Einfache Programme erledigen meist nur einfachste Aufgaben, aber die Anforderungen sind oft komplexer.
  • So wird man umfangreiche awk-Programme in separate Dateien schreiben, wie man in der Shell-Programmierung komplexere Konstrukte in Shell-Skripten formuliert.

Awk bezieht seine Instruktionen aus einer Steuerdatei, wenn deren Namen explizit mit der Option -f angegeben wurde:

awk -f <Programm.awk> <Datei> [<Datei>]

Bei Verwendung einer Pipe:

Kommando | awk -f <Programm.awk>
  • Awk-Anwendungen, die man immer wieder benötigt, wird man bevorzugt in Dateien fassen; Beispiele werden uns im weiteren noch reichlich begegnen.
  • Noch einfacher gestaltet sich der awk-Programmaufruf bei Verwendung von awk-Skripten.
  • Man bedient sich der Aufrufsyntax der entsprechenden UNIX Shell und weist die Shell an, die nachfolgenden Anweisungen dem Awk-Interpreter zuzuführen.
  • Zuoberst muss einem awk-Skript nur die Zeile #!/usr/bin/awk -f (bzw. der korrekte Zugriffspfad zum awk-Programm) stehen.

Versieht man eine solche Datei noch mit den entsprechenden Ausführungsrechten (»chmod u+x Programm.awk«), genügt ein simpler Aufruf:

<Programm.awk> <Datei>

Bei Verwendung einer Pipe:

Kommando | <Programm.awk>

Tipp

  • Wenn Sie beim Editieren eines awk-Skripts vim oder kate verwenden, so stellt dieser Editor den Text mit Syntax-Highligthing dar, wenn der Skriptname auf ».awk« endet.
  • Davon abgesehen, ist die Namensgebung des Skripts Ihnen überlassen.

Kommandozeilenoptionen

-F Feldtrenner awk arbeitet auf Feldern einer Eingabezeile.
  • Normalerweise dient das Leerzeichen/Tabulator zur Trennung einzelner Felder.
  • Mit der Option -F wird der Wert der internen Variable FS (field separator) verändert.
  • Das bereits erwähnte Beispiel zur Ausgabe des ersten Feldes der Passwortdatei, indem das BEGIN-Muster zum Setzen des Feldtrenners genutzt wurde, lässt sich somit auch wie folgt realisieren:
$ awk -F : '{print NR,$1}' /etc/passwd
1 root
2 bin
3 daemon
4 lp
5 news
…
-v Variable=Wert Eine im Programm verwendete Variable kann somit »von außen« initialisiert werden (eine interne Initialisierung wird damit nicht überschrieben).
-f Programmdatei awk liest den Quellcode aus der angegebenen Datei.
-c / - -traditional GNU awk verhält sich wie UNIX awk, d.h.
  • die GNU-Erweiterungen werden nicht akzeptiert.
-h / --help Eine Kurzhilfe erscheint.
-P / - -posix Schaltet den compatibility mode mit folgenden zusätzlichen Einschränkungen an:* \x escape sequences werden nicht erkannt
  • Nur space und tab agieren als field separators wenn FS auf ein einzelnes Leerzeichen gesetzt ist; newline nicht
  • Fortsetzung der Zeile nach ? und :. ist nicht möglich
  • Das Synonym func für das Schlüsselwort function wird nicht erkannt
  • Die Operatoren ** und **= können nicht statt ^ und ^= genutzt werden
  • Die fflush() Funktion ist nicht verfügbar

Kommandozeilenparameter

Awk' s Umgang mit Kommandozeilenparametern ist etwas eigenwillig.

  • C-Programmierern sind sicherlich die Variablennamen argc und argv geläufig, die bevorzugt gewählt werden, um Kommandozeilenargumente an das Hauptprogramm zu übergeben.
  • Awk verfügt über zwei builtin-Variablen ARGC und ARGV, die die Anzahl auf der Kommandozeile stehenden Parameter (ARGC) angeben und den Zugriff darauf über ein Feld (ARGV) ermöglichen.
  • Das verwirrende daran ist, dass Awk seine eigenen Kommandozeilenoptionen nicht mitzählt.

Beispiel

#!/usr/bin/awk -f
BEGIN {
print "Anzahl Argumente: ", ARGC;
for (i=0; i < ARGC; i++)
print i, ".Argument: ", ARGV[i];
}

(arguments.awk)

  • Wir verzichten jetzt auf die Beschreibung der verwendeter Kontrollkonstrukte und eingebauter Variablen und kommen später darauf zurück.

Reihenfolge der Argumente

Achte in den folgenden Testläufen auf die Reihenfolge der Argumente

$ ./arguments.awk -F : --posix n=3 "Berlin"
Anzahl Argumente: 3
0. Argument: awk
1. Argument: n=3
2. Argument: Berlin
$ ./arguments.awk n=3 -F : --posix "Berlin"
Anzahl Argumente: 7
0 .Argument: awk
1 .Argument: n=3
2 .Argument: -F :
3 .Argument: --posix
4 .Argument: Berlin

Abzuleitende Regeln

Aus dem Beispiel sind mehrere Regeln abzuleiten

  1. ARGC ist mindestens 1 (da der Programmname immer als erstes Argument übergeben wird)
  2. Die Indizierung der Argumente in ARGV beginnt bei 0
  3. Awk übernimmt die eigenen Aufrufoptionen nicht in die Argumentenliste
  4. Sobald Awk ein Argument als »nicht-eigene Option« erkennt, behandelt es alle weiteren Argumente als »fremde Optionen« (diese verlieren damit auch ihre übliche Wirkung)
  5. Für Awk ist jedes Argument zunächst der Name einer Datei
  • Die letzte Eigenschaft ist sofort anhand von Fehlermeldungen ersichtlich, sobald das Beispielprogramm »normale Anweisungen« (außer BEGIN und END) umfasst.
  • Argumente würden allerdings jeglichen Nutzen entbehren, ließen sie sich nicht doch vor Awk's Interpretation schützen.
  • Da Awk die so übergebenen Dateien erst mit dem Eintritt in die Hauptschleife zu öffnen versucht, bleibt das BEGIN-Muster als der Ort der Einflussnahme übrig.
  • Ein Argument sollte hier ausgewertet und anschließend aus dem Array ARGV gelöscht werden.

Das folgende Beispiel nutzt das erste Kommandozeilenargument, um die eingebaute Variable FS neu zu belegen:

#!/usr/bin/awk -f
BEGIN {
if (ARGC > 1) {
FS=ARGV[1];
delete ARGV[1]
}
}
...
  • Nehmen Sie das Beispiel nicht zu ernst...
  • den Field Separator FS würde jeder erfahrene Awk-Programmierer mittels der Option -F setzen.
  • Überhaupt ist im Beispiel die Annahme einer festen Position des Arguments eine unsaubere Methode und sollte vermieden werden.
  • Besser wäre eine positionsunabhängige Behandlung aller Argumente.

Initiale Werte

  • Dienen Argumente einzig dazu, im Programm verwendeten Variablen initiale Werte zuzuweisen, so kann dies vorteilhaft erfolgen, indem dem Variablennamen auf der Kommandozeile der Startwert per Gleichheitszeichen zugewiesen wird.
  • Allerdings stehen derartige Variablen erst in der Hauptschleife und nicht im BEGIN-Block zur Verfügung (es sei denn, auf sie wird über ARGV zugegriffen).
  • Als Beispiel dient eine awk-basierte Variante des Kommandos »head«, das die ersten n Zeilen der Eingabe (10 in der Voreinstellung) ausgibt:
#!/usr/bin/awk -f

BEGIN {
n=10;
}

{ if (n < FNR) exit; }
{ print $0; }

Speicherten wir nun das Programm unter dem Namen »head.awk« (chmod nicht vergessen!) und bringen es ohne weitere Argumente zur Ausführung, so werden - gemäß der Voreinstellung »n=10« im BEGIN-Muster - die ersten 10 Zeilen der Eingabedatei ausgegeben:

$ ./head.awk /etc/services 
#
# Network services, Internet style
#
# Note that it is presently the policy of IANA to assign a single well-known
# port number for both TCP and UDP; hence, most entries here have two entries
# even if the protocol doesn't support UDP operations.
#
# This list could be found on:
#  http://www.iana.org/assignments/port-numbers
#

Um eine abweichende Anzahl Zeilen zur Ausgabe zu bringen, muss der Variablen n beim Kommandoaufruf der entsprechenden Wert mitgegeben werden:

$ ./head.awk n=2 /etc/services
#
# Network services, Internet style
  • Die Zuweisung an die Variable muss vor dem Dateinamen stehen; im anderen Fall wäre n noch nicht bekannt, wenn Awk die Datei betrachtet.
  • Mehrere Variablen lassen sich durch Leerzeichen getrennt angeben.
  • Zwischen Variablennamen und Wert darf sich nur das Gleichheitszeichen befinden (auch keine Leerzeichen!):

Syntaxfehler

$ ./head.awk n = 2 /etc/services
awk: ./head.awk:4: Fatal: Die Datei »n«�kann nicht zum Lesen geöffnet werden (Datei oder Verzeichnis nicht gefunden)