Awk: Unterschied zwischen den Versionen

Aus Linupedia.org
Wechseln zu: Navigation, Suche
(Beispiele Aktion +)
(Überarbeitung und Freigabe)
Zeile 1: Zeile 1:
{{UnderConstruction}} [[Benutzer:Robi|Robi]] 18:38, 26. Sep. 2008 (UTC)
+
''' AWK '''-
 
+
eine kleine Einführung und Übersicht über ein mächtiges UNIX-Werkzeug
Hier wird in nächster Zeit eine kleine Einführung in awk enstehen. Wegen der Mächtigkeit und des Gesamtumfanges wird auf eine tiefgründige Aufarbeitung des Themas verzichtet.
 
 
 
 
 
  
  
Zeile 11: Zeile 8:
  
 
awk wurde in den Haupzügen schon in den 70/80 Jahren des vorigen Jahrhunderts entwickelt und zählt zu den universellen UNIX-Standard-Werkzeugen. Es besitzt eine fest definierten Spachumfang und läßt sich nicht wie andere Progammierspachen fast beliebig erweitern. Dafür ist es jedoch sehr portabel und heute auf vielen Plattformen zu Hause. Obwohl heute Spachen wie Perl viele frühere Anwendungsbereiche von awk besser und schneller erledigen könnten, wird auch heute noch vielfach auf awk zurückgegriffen. Es gibt heute eine Reihe unterschiedlicher AWK-Implementierungen mit differierendem Leistungsumfang, auf Linux-Systemen ist das sehr leistungsfähige '''[http://www.gnu.org/software/gawk/ Gawk]''' der Standard. Speziell kleinere awk-Programme lassen sich gut in Shell-Skripte einbetten, was häufig genutzt wird.       
 
awk wurde in den Haupzügen schon in den 70/80 Jahren des vorigen Jahrhunderts entwickelt und zählt zu den universellen UNIX-Standard-Werkzeugen. Es besitzt eine fest definierten Spachumfang und läßt sich nicht wie andere Progammierspachen fast beliebig erweitern. Dafür ist es jedoch sehr portabel und heute auf vielen Plattformen zu Hause. Obwohl heute Spachen wie Perl viele frühere Anwendungsbereiche von awk besser und schneller erledigen könnten, wird auch heute noch vielfach auf awk zurückgegriffen. Es gibt heute eine Reihe unterschiedlicher AWK-Implementierungen mit differierendem Leistungsumfang, auf Linux-Systemen ist das sehr leistungsfähige '''[http://www.gnu.org/software/gawk/ Gawk]''' der Standard. Speziell kleinere awk-Programme lassen sich gut in Shell-Skripte einbetten, was häufig genutzt wird.       
 +
  
 
Das Grundprinzip von awk beruht auf der Verarbeitung einer Datei oder wenn nicht angegeben automatisch der Standardeingabe, dieser wird zeilenweise nach anwenderspezifischen Mustern durchsucht und beim Auffinden von Übereinstimmung eine dem jeweiligem Muster zugeordnete Aktion ausgeführt. Jeder Datensatz wird automatisch in Felder zerlegt, auf die man bequem zugreifen kann. Das Ergebnis wird in der Standardausgabe ausgegeben oder kann entsprechend umgeleitet werden. Die Eingabedatei wird nicht verändert. Sowohl Ein- wie auch Ausgabe wird dabei von awk als eine Folge von durch Zeilentrennzeichen separierte Zeilen (records) angesehen, die ihrerseits wieder durch Feldtrennzeichen in einzelne Felder aufgeteilt sind. Sowohl Zeilentrennzeichen (default ASCII-LF) als auch Feldtrennzeichen ( default Leerzeichen oder Tabulatorzeichen) können vom Anwender auch individuell angepasst werden. Eine Eingabezeile kann als Ganzes mit dem Bezeichner '''$0''' angesprochen werden, die einzelnen Felder der Zeile mit '''$1 , $2 , $3 .... $n'''. Besonders geeignet sind somit Dateien in Listen- oder Tabellenform oder analog die Ausgaben von Befehlen in diesem Format, und davon gibt es auf jedem Rechner jede Menge.
 
Das Grundprinzip von awk beruht auf der Verarbeitung einer Datei oder wenn nicht angegeben automatisch der Standardeingabe, dieser wird zeilenweise nach anwenderspezifischen Mustern durchsucht und beim Auffinden von Übereinstimmung eine dem jeweiligem Muster zugeordnete Aktion ausgeführt. Jeder Datensatz wird automatisch in Felder zerlegt, auf die man bequem zugreifen kann. Das Ergebnis wird in der Standardausgabe ausgegeben oder kann entsprechend umgeleitet werden. Die Eingabedatei wird nicht verändert. Sowohl Ein- wie auch Ausgabe wird dabei von awk als eine Folge von durch Zeilentrennzeichen separierte Zeilen (records) angesehen, die ihrerseits wieder durch Feldtrennzeichen in einzelne Felder aufgeteilt sind. Sowohl Zeilentrennzeichen (default ASCII-LF) als auch Feldtrennzeichen ( default Leerzeichen oder Tabulatorzeichen) können vom Anwender auch individuell angepasst werden. Eine Eingabezeile kann als Ganzes mit dem Bezeichner '''$0''' angesprochen werden, die einzelnen Felder der Zeile mit '''$1 , $2 , $3 .... $n'''. Besonders geeignet sind somit Dateien in Listen- oder Tabellenform oder analog die Ausgaben von Befehlen in diesem Format, und davon gibt es auf jedem Rechner jede Menge.
Zeile 21: Zeile 19:
  
  
; Datenauswertung und -aufbereitung:
+
*; Datenauswertung und -aufbereitung:
 
Vor allem bei Anwendungen mit Listenausgabe, dabei sind uA. Filteroptionen und statistische Problemstellungen, wie z.B. Berechnung oder einfach nur Summierung von Feldinhalten oder Zählung von Häufigkeiten, in Verbindung mit weiteren Auswahlkriterien möglich
 
Vor allem bei Anwendungen mit Listenausgabe, dabei sind uA. Filteroptionen und statistische Problemstellungen, wie z.B. Berechnung oder einfach nur Summierung von Feldinhalten oder Zählung von Häufigkeiten, in Verbindung mit weiteren Auswahlkriterien möglich
; Datentransformation:
+
 
 +
 
 +
*; Datentransformation:
 
Eingabedaten werden auf beliebige Weise neu gruppiert, neu formatiert oder selektiv verändert.  
 
Eingabedaten werden auf beliebige Weise neu gruppiert, neu formatiert oder selektiv verändert.  
; Datenvalidierung:
+
 
 +
 
 +
*; Datenvalidierung:
 
Überprüfung der Daten auf ihre syntaktische und semantische Korrektheit. Auch komplettes Neuerstellen von Konfigurationsdateien aus vom User editierten Dateien.
 
Überprüfung der Daten auf ihre syntaktische und semantische Korrektheit. Auch komplettes Neuerstellen von Konfigurationsdateien aus vom User editierten Dateien.
  
Zeile 45: Zeile 47:
 
  awk 'Programmtext' Eingabedatei
 
  awk 'Programmtext' Eingabedatei
 
; Programmtext vor Interpretation durch die Shell geschütz direkt hinter den Programmaufruf :
 
; Programmtext vor Interpretation durch die Shell geschütz direkt hinter den Programmaufruf :
Diese Form ist besonders für klein und kleinste awk Programme geeignet, da im Falle eines Syntaxfehlers der gesamte awk Programmtext in der Komandozeile wieder neu eingegeben werden muss, und man oftmals auf hilfreiche übersichtliche Formatierung des awk Programmtextes verzichten muss.
+
Diese Form ist besonders für klein und kleinste awk Programme geeignet. Im Falle eines Syntaxfehlers muss der gesamte awk Programmtext in der Komandozeile wieder neu erstellt oder eingegeben werden, das ist bei awk-Programmen die wesentlich länger als eine Zeile sind, selbst mit einer intelligenten Shell natürlich ungeschickt.  Oftmals muss man auch auf hilfreiche übersichtliche Formatierung des awk Programmtextes verzichten, bei kleinsten Programmen erweist sich das nicht als Problem.
  
  
  
Beide Aufrufmöglichkeiten lassen sich in Shellscripten verwenden. Wobei oftmals innerhalb von Shellscripten die 2. Variante bevorzugt wird, da damit das awk Programm mit dem Shellscipt gleichzeitig entwickelt werden kann und dann auch beides als eine Einheit in einer einzigen Datei enthalten ist. Bei universalen awk Programmtexten, die von mehreren Scripten aufgerufen werden können, bietet sich jedoch die erste Variante besser an.
+
Beide Aufrufmöglichkeiten lassen sich in Shellscripten verwenden. Wobei oftmals innerhalb von Shellscripten die 2. Variante bevorzugt wird, da damit das awk Programm mit dem Shellscipt gleichzeitig entwickelt werden kann und hat dann auch beides als eine übersichtliche Einheit in einer einzigen Datei. Bei universalen awk Programmtexten, die eventuell auch von mehreren Scripten aufgerufen werden können, bietet sich jedoch die erste Variante besser an.
  
 
   
 
   
Zeile 69: Zeile 71:
  
 
* '''BEGIN''' und '''END''' sind Schlüsselworte für spezielle Bedingungen innerhalb des Programmablauf, deren Aktionen werden entweder vor oder nach der Bearbeitung der Datei ausgeführt. BEGIN oder END Aktionen sind optional, also nicht jedes awk Programm muss sie beinhalten.
 
* '''BEGIN''' und '''END''' sind Schlüsselworte für spezielle Bedingungen innerhalb des Programmablauf, deren Aktionen werden entweder vor oder nach der Bearbeitung der Datei ausgeführt. BEGIN oder END Aktionen sind optional, also nicht jedes awk Programm muss sie beinhalten.
* Um die Bedingungen von den Aktionen unterscheiden zu können, werden die Aktionen in geschweifte Klammern '''{  }''' eingeschlossen. Mehrere Aktionen können durch ''';''' oder durch einen Zeilentrennzeichen getrennt werden.
+
* Um die Bedingungen von den Aktionen unterscheiden zu können, werden die Aktionen in geschweifte Klammern '''{  }''' eingeschlossen.
 +
* Mehrere Aktionen können durch ''';''' oder durch einen Zeilentrennzeichen getrennt werden.
 
* Bedingungungen dürfen nicht über mehrere Zeilen geschrieben werden.
 
* Bedingungungen dürfen nicht über mehrere Zeilen geschrieben werden.
* ein Aktion müssen in derselben Programmzeile beginnen in der die Bedingung steht ( "{" reicht aus ) dürfen aber über mehrere Zeilen fortgesetzt werden.
+
* ein Aktion müssen in derselben Programmzeile beginnen, in der die Bedingung steht ( "'''{'''" reicht aus ) dürfen aber über mehrere Zeilen fortgesetzt werden.
 
* bei einer fehlenden Bedingung, wird die Aktion für jede Zeile der Eingabedatei ausgeführt.
 
* bei einer fehlenden Bedingung, wird die Aktion für jede Zeile der Eingabedatei ausgeführt.
 
* bei einer fehlenden Aktion, wird jede zur Bedingung passenden Zeile unverändert an die Ausgabe durchgereicht.
 
* bei einer fehlenden Aktion, wird jede zur Bedingung passenden Zeile unverändert an die Ausgabe durchgereicht.
Zeile 95: Zeile 98:
  
 
Wie oben schon beschrieben, legt eine Bedingung fest, ob für eine Zeile die dazugehörige Aktion auszuführen ist oder nicht.
 
Wie oben schon beschrieben, legt eine Bedingung fest, ob für eine Zeile die dazugehörige Aktion auszuführen ist oder nicht.
In awk gibt es 2 prinzipielle Möglichkeiten für Bedingungen, einmal '''[[Reguläre Ausdrücke]]''' und zum anderem '''Vergleichsausdrücke'''. Reguläre Ausdrücke müssen in Schrägstriche '''/..../''' eingeschlossen werden.  Dabei kann sowohl auf den Inhalt der ganzen Zeile sowie auf deren einzelnen Felder wie auch auf die Variablen innerhalb des Programmes zugegriffen werden. Darüber hinaus ist es möglich mit logischen Verknüpfungen und Bereichen (analog etwa von - bis) zu arbeiten. Die Leistungsfähigkeit und Möglichkeiten von awk gehen hier weit über das hinaus, was andere Scriptsprachen, Tools und Programme können. An dieser Stelle nur ein paar Beispiele.
+
In awk gibt es 2 prinzipielle Möglichkeiten für Bedingungen, einmal '''[[Reguläre Ausdrücke]]''' und zum anderem '''Vergleichsausdrücke'''. Reguläre Ausdrücke müssen in Schrägstriche '''/..../''' eingeschlossen werden.  Dabei kann sowohl auf den Inhalt der ganzen Zeile sowie auf deren einzelnen Felder wie auch auf die Variablen innerhalb des Programmes zugegriffen werden. Darüber hinaus ist es möglich mit logischen Verknüpfungen und Bereichen (analog etwa von - bis) zu arbeiten. Die Leistungsfähigkeit und Möglichkeiten von awk gehen hier weit über das hinaus, was andere Scriptsprachen, Tools und Programme können. An dieser Stelle nur ein paar einfache Beispiele.
 
<pre>
 
<pre>
 
/Kasten|Kiste/ {print $0}          # alle Zeilen die das "Kasten" oder "Kisten" enthalten
 
/Kasten|Kiste/ {print $0}          # alle Zeilen die das "Kasten" oder "Kisten" enthalten
Zeile 117: Zeile 120:
 
=== Die Aktion ===
 
=== Die Aktion ===
  
Mit der Aktion werden im Wesentlichen 2 Dinge getätigt, zum einem werden hier die Ausgaben erstellt, und zum anderen können hier die Variablen während des Programmablaufs geändert also zB abhängig von Werten innerhalb der Zeile neu berechnet werden. Eine Aktion kann ihrerseits wieder mit Hilfe von Bedingungen oder Schleifen gesteuert werden. Die Ausgabe ist die wesentliche Aufgabe der Aktionen, denn ohne eine einzige Ausgabe würde unser awk Programm ja gar nichts machen. Die einfachste Form der Ausgabe ist die Funktion '''print''' darüber hinaus sind aber weitere sehr leistungsfähige Printfunktionen analog der [http://de.wikipedia.org/wiki/C_(Programmiersprache) Programmiersprache C] vorhanden.  An dieser Stelle nur einige einfache Ausgabe-Beispiele
+
Mit der Aktion werden im wesentlichen 2 Dinge getätigt, zum einem werden hier die Ausgaben erstellt, und zum anderen können hier die Variablen während des Programmablaufs geändert also zB abhängig von Werten innerhalb der Zeile neu berechnet werden. Eine Aktion kann ihrerseits wieder mit Hilfe von Bedingungen oder Schleifen gesteuert werden. <br>
 +
Die Ausgabe ist die wesentliche Aufgabe der Aktionen, in jedem awk Programm gibt es mindesdens eine Ausgabeaktion, denn ohne eine einzige Ausgabe würde unser awk Programm ja gar nichts machen. Die einfachste Form der Ausgabe ist die Funktion '''print''' darüber hinaus sind aber weitere sehr leistungsfähige Printfunktionen analog der [http://de.wikipedia.org/wiki/C_(Programmiersprache) Programmiersprache C] vorhanden.  An dieser Stelle nur einige einfache Ausgabe-Beispiele, mit den entsprechenden Ausgaben.
 
<pre>  
 
<pre>  
 
LINUX:/tmp # echo "1 2 3 4 5" | awk '{print $0}'           
 
LINUX:/tmp # echo "1 2 3 4 5" | awk '{print $0}'           
Zeile 123: Zeile 127:
 
LINUX:/tmp # echo "1 2 3 4 5" | awk '{print $1,$3,$5}'
 
LINUX:/tmp # echo "1 2 3 4 5" | awk '{print $1,$3,$5}'
 
1 3 5
 
1 3 5
 +
LINUX:/tmp # echo "1 2 3 4 5" | awk '{OFS=":";print $1,$3,$5}'
 +
1:3:5
 
LINUX:/tmp # echo "1 2 3 4 5" | awk '{print $1 $3 $5}'
 
LINUX:/tmp # echo "1 2 3 4 5" | awk '{print $1 $3 $5}'
 
135
 
135
Zeile 145: Zeile 151:
 
eine kleine Spielerei noch zum Abschluss nur um zu verdeutlichen was mit wenigen Aktionen mit awk machbar ist.
 
eine kleine Spielerei noch zum Abschluss nur um zu verdeutlichen was mit wenigen Aktionen mit awk machbar ist.
 
<pre>
 
<pre>
cat buch.txt | awk '{for (i=1;i<=NF;i++)anzahl[$i]++}
+
awk '{for (i=1;i<=NF;i++)anzahl[$i]++} END {for (i in anzahl) print anzahl[i],i}' buch.txt
END{for(i in anzahl) print i, anzahl[i]}'
 
 
</pre>
 
</pre>
würde die einzelnen Worte der Datei '''buch.txt''' statistisch zählen und ausgeben.
+
würde die einzelnen Worte der Datei '''buch.txt''' erfassen, statistisch zählen und im Format "Anzahl Wort" ausgeben. Ein '''sort -n''' könnte uns das dann anschließend noch sortieren
 +
 
 +
 
 +
 
 +
 
  
 
== Einige einfache Beispiele mit Erläuterungen ==
 
== Einige einfache Beispiele mit Erläuterungen ==
Zeile 158: Zeile 167:
 
=== Beispiel 1 ===
 
=== Beispiel 1 ===
 
  BEFEHL | awk '{print $3}' | BEFEHL
 
  BEFEHL | awk '{print $3}' | BEFEHL
Diesem sehr einfachen awk Befehlskonstrukt werden wir in vielen Scipten wiederfinden. Sie unterscheiden sich nur in der Zahl hinter dem $. Die Ausgabe eines Befehles wird mittels Pipe an awk übergeben. Die einfachen Hochkomma dienen hier nur dem Schutz vor der Interpretation der Shell so das unser ganzer awk Programmtext nur aus '''{print $3}''' besteht. Die Ausgabe wird anschließend wiederum an den nächsten Befehl mittels Pipe weitergereicht.
+
Diesem sehr einfachen awk Befehlskonstrukt werden wir in vielen Scipten wiederfinden. Sie unterscheiden sich nur in der Zahl hinter dem $. <br>
 +
Die Ausgabe eines Befehles wird mittels Pipe an awk übergeben. Die einfachen Hochkomma dienen hier nur dem Schutz vor der Interpretation der Shell so das unser ganzer awk Programmtext nur aus '''{print $3}''' besteht. Die Ausgabe wird anschließend wiederum an den nächsten Befehl mittels Pipe weitergereicht.
  
'''{print $3}''' wir haben hier nur eine Aktion ohne eine einzige Bedingung, sie wird also mit jeder Zeile der Eingabedatei durchgeführt. Die Aktion ist hier im Beispiel einfach nur "'''gebe für diese Zeile das Feld Nr. 3 zu Ausgabe'''". Also wir schneiden von jeder Zeile alles weg, außer Feld Nr. 3. Warum nimmt man dann nicht den Befehl '''[http://linux.die.net/man/1/cut cut]''' mit dem geht doch das auch ?
+
'''{print $3}''' wir haben hier nur eine Aktion ohne eine einzige Bedingung, sie wird also mit jeder Zeile der Eingabedatei durchgeführt. Die Aktion bedeutet hier im Beispiel einfach nur "'''gebe für diese Zeile das Feld Nr. 3 zu Ausgabe'''". Also wir schneiden von jeder Zeile alles weg, außer Feld Nr. 3. Warum nimmt man dann nicht den Befehl '''[http://linux.die.net/man/1/cut cut]''' mit dem geht doch das auch ?
  
Die Antwort ist sehr einfach, '''cut''' kennt nur jeweils einen einzigen Feldtrenner, '''awk''' arbeitet hier mit mehr Feldtrennern und geht damit viel intelligenter um als die sturen Regeln in '''cut'''. Wir wissen nicht was der User bei editieren einer Datei zwischen die einzelnen Felder für Feldtrenner einbaut, ein Leerzeichen, mehrere Leerzeichen, ein Tab-Zeichen oder mehrere, oder gar Tab- und Leerzeichen gemischt. Für den User zählt hier einzig das äußere Erscheinungsbild der Datei, bei '''cut''' müssten wir hier ganz schön vorarbeiten, damit wir auch sicher genau das 3 Feld erwischen, egal was der User eingegeben hat. '''awk''' macht das im Schlaf, ein direkter Vergleich zwischen '''awk''' und '''cut''' soll das belegen.
+
Die Antwort ist sehr einfach, '''cut''' kennt nur jeweils einen einzigen Feldtrenner, '''awk''' arbeitet hier mit mehr Feldtrennern und geht damit viel intelligenter um als die sturen Regeln in '''cut'''. Wir wissen nicht was ein User beim editieren einer Datei zwischen die einzelnen Felder für Feldtrenner einbaut, ein Leerzeichen? mehrere Leerzeichen? ein Tab-Zeichen oder mehrere? oder gar Tab- und Leerzeichen gemischt? Für den User zählt hier einzig das äußere Erscheinungsbild der Datei, bei '''cut''' müssten wir hier ganz schön vorarbeiten, damit wir auch sicher genau das richtige Feld erwischen, egal was der User eingegeben hat. '''awk''' macht das im Schlaf, ein direkter Vergleich zwischen '''awk''' und '''cut''' soll das belegen.
 
<pre>
 
<pre>
 
# echo -e "1  2 3\t4 \t \t 5" | awk '{print $1,$2,$3,$4,$5}'
 
# echo -e "1  2 3\t4 \t \t 5" | awk '{print $1,$2,$3,$4,$5}'
Zeile 177: Zeile 187:
 
4
 
4
 
</pre>
 
</pre>
 +
 +
  
  
 
==== Variationen ====
 
==== Variationen ====
 
  BEFEHL | awk '" " { print $2 }' | BEFEHL
 
  BEFEHL | awk '" " { print $2 }' | BEFEHL
hier handelt es sich um genau das selbe, nur wird hier zusätzlich noch einmal deutlich gemacht, dass hier eine leere also keine Bedingung steht.
+
hier handelt es sich um genau das selbe, nur wird hier zusätzlich noch einmal deutlich gemacht, dass hier eine leere, also keine Bedingung steht.
 
  BEFEHL | awk -F" " '{ print $8}' | BEFEHL
 
  BEFEHL | awk -F" " '{ print $8}' | BEFEHL
 
  BEFEHL | awk -F[ '{print $1}' | BEFEHL
 
  BEFEHL | awk -F[ '{print $1}' | BEFEHL
 
  BEFEHL | awk -F. '{print $1 , $2 , $3}' | BEFEHL
 
  BEFEHL | awk -F. '{print $1 , $2 , $3}' | BEFEHL
hier wird mit Hilfe der Option '''-F''' ein spezieller Feldtrenner für die Eingabedaten festgelegt. Im letzten Befehl wird nicht nur ein Feld ausgegeben sondern die Felder 1 2 und 3 und als Feldtrennezeichen der Ausgabe wird das Standardzeichen " " von awk verwendet. ''( '''Achtung''' wird hier '''{print $1 $2 $3}''' verwendet, dann werden die Felder ohne Trennzeichen ausgegeben) ''  
+
hier wird mit Hilfe der Option '''-F''' ein spezieller Feldtrenner für die Eingabedaten festgelegt. Im letzten Befehl wird nicht nur ein Feld ausgegeben sondern die Felder 1 2 und 3 und als Feldtrennezeichen der Ausgabe wird das Standardzeichen " " von awk verwendet. ''( '''Achtung''' wird hier '''{print $1 $2 $3}''' verwendet, dann werden die Felder ohne Trennzeichen ausgegeben) siehe auch obrige Beispiele ''  
 
  VARIABLE=`BEFEHL | awk '{print $1 * 60}'`
 
  VARIABLE=`BEFEHL | awk '{print $1 * 60}'`
 
  VARIABLE=`BEFEHL | awk -F: '{print $1 / $2 * $3 + 1}'`
 
  VARIABLE=`BEFEHL | awk -F: '{print $1 / $2 * $3 + 1}'`
hier wird mit den Feldern vor der Ausgabe gerechnet im ersten Fall wird der 60fache Wert von Feld 1 und im zweiten Fall der Wert von
+
hier wird mit den Feldern vor der Ausgabe gerechnet im ersten Fall wird der 60fache Wert von Feld 1 <br>
'''Feld1 / Feld2 * Feld3 + 1''' errechnet und ausgegeben und somit in der Variable gepeichert
+
und im zweiten Fall der Wert von '''Feld1 / Feld2 * Feld3 + 1''' errechnet und ausgegeben und somit in der Variable gepeichert
 
  VARIABLE=`BEFEHL | awk '{ORS=""; print $1}'`
 
  VARIABLE=`BEFEHL | awk '{ORS=""; print $1}'`
hierbei handelt es sich um eine Anpassung des Ausgabeformates, die implementierte Variable '''ORS''' in awk beinhaltet das Zeilentrennzeichen der Ausgabe, also normalerweise ''''\n''''. In diesem Falle wurde es geleert, das bedeutet in der Ausgabe dieser Aktion gibt es keine Zeilenvorschub, die nächste Aktion würde direkt an das Ende der Ausgabe weiterschreiben.
+
hierbei handelt es sich um eine Anpassung des Ausgabeformates, die implementierte Variable '''ORS''' in awk beinhaltet das Zeilentrennzeichen der Ausgabe, also normalerweise ''''\n''''. In diesem Falle wurde es geleert, das bedeutet in der Ausgabe dieser Aktion gibt es keinen Zeilenvorschub, die nächste Aktion würde direkt an das Ende der Ausgabe weiterschreiben.
 
  BEFEHL | awk '{OFS=";"; print $1,$3,$8}'
 
  BEFEHL | awk '{OFS=";"; print $1,$3,$8}'
hierbei handelt es sich ebenfalls um eine Anpassung des Ausgabeformates, nur wird hier die implementierte Variable '''OFS''' geändert, in ihr steht das Feldtrennzeichen der Ausgabe, also normalerweise '''" "'''. Hier werden also für jede Zeile der Eingabe die Felder 1 3 und 8 mit einem''' ;''' getrennt ausgegeben.
+
hierbei handelt es sich ebenfalls um eine Anpassung des Ausgabeformates, nur wird hier die implementierte Variable '''OFS''' geändert, in ihr steht das Feldtrennzeichen der Ausgabe, also normalerweise ein Leerzeichen. Hier werden also für jede Zeile der Eingabe die Felder 1 3 und 8 mit einem''' ;''' getrennt ausgegeben.
  
  
Zeile 206: Zeile 218:
 
==== Variationen ====
 
==== Variationen ====
 
  BEFEHL | awk -F= '/quant=/ {print $2}' | BEFEHL  
 
  BEFEHL | awk -F= '/quant=/ {print $2}' | BEFEHL  
das kennen wir schon, es wurde nur zusätzlich noch ein anderes Zeilentrennzeichen definiert
+
das kennen wir schon, es wurde nur zusätzlich noch ein anderes Feldtrennzeichen für die Eingabe definiert
 
  BEFEHL | awk '/unit/ {print sum+=$2}' | BEFEHL
 
  BEFEHL | awk '/unit/ {print sum+=$2}' | BEFEHL
 
Diese Variante hat hier eine Neuerung für uns, es wird hier nicht der Inhalt eines Feldes selbst ausgegeben, sondern der Inhalt einer benutzerdefinierten Variable. '''sum''' <br>
 
Diese Variante hat hier eine Neuerung für uns, es wird hier nicht der Inhalt eines Feldes selbst ausgegeben, sondern der Inhalt einer benutzerdefinierten Variable. '''sum''' <br>
 
Die Variable '''sum''' hat beim Programmstart den Wert 0 (weil nicht anders initalisiert)<br>   
 
Die Variable '''sum''' hat beim Programmstart den Wert 0 (weil nicht anders initalisiert)<br>   
Mit jeder Zeile in der der Suchstring "'''unit'''" enthalten ist, wird zu diesem Wert der Wert von Feld 2 dazuaddiert und dann diese Summe ausgegeben.<br>
+
Mit jeder Zeile in der der Suchstring "'''unit'''" enthalten ist, wird zu diesem Wert jeweils der Wert von Feld2 dazuaddiert und dann diese Summe ausgegeben.<br>
Bei mehreren gefundenen Zeilen mit "'''unit'''" wird unser awk Programm also eine entsprechende Zahlenreihe ausgeben, die jeweils um den Wert des Feldes 2 der betreffenden Zeilen ansteigt.
+
Bei mehreren gefundenen Zeilen mit "'''unit'''" wird unser awk Programm also eine entsprechende Zahlenreihe ausgeben, die jeweils um den Wert des Feldes2 der betreffenden Zeilen ansteigt.
 
  $hdparm -d /dev/dvd | awk '$1 == "using_dma" {print $3}'  
 
  $hdparm -d /dev/dvd | awk '$1 == "using_dma" {print $3}'  
hier handelt es sich um das selbe Prinzip, nur wurde hier als Bedingung kein Regulärer Ausdruck verwendet, sondern ein Vergleichsausdruck, es wird hier geprüft ob der Inhalt von Feld 1 gleich "'''using_dma'''" entspricht, und nur in diesem Fall wird das Feld 3 ausgegeben.
+
hier handelt es sich um das selbe Prinzip, nur wurde hier als Bedingung kein Regulärer Ausdruck verwendet, sondern ein Vergleichsausdruck, es wird hier geprüft ob der Inhalt von Feld 1 gleich "'''using_dma'''" entspricht, und nur in diesem Fall wird das Feld3 ausgegeben.
  
  
Zeile 220: Zeile 232:
 
=== Beispiel 3 ===
 
=== Beispiel 3 ===
 
  BEFEHL | awk '{for (i=1;i<=NF;i++){print $i}}' | BEFEHL
 
  BEFEHL | awk '{for (i=1;i<=NF;i++){print $i}}' | BEFEHL
Dieser oder ein ähnlicher awk Befehl wird in einigen Scripten verwendet. Es gibt keine Bedingung also wird für jede Zeile die Aktion gestartet. Auf dem ersten Blick ist hier schon die Verwandschaft von C zu erkennen.<br>
+
Dieser oder ein ähnlicher awk Befehl wird in einigen Scripten verwendet. Es gibt keine Bedingung also wird für jede Zeile die Aktion gestartet. ''( Die Schreibweise der for-Schleife zeigt hier auf den ersten Blick schon die Verwandschaft von '''C''' ).''<br>
  
Diese Aktion besteht aus einer FOR-Schleife in der der Wert einer Variable '''i''' mit dem Ausgangswert von '''1''' hochgezählt wird und als Abbruchkriterium der Wert von '''NF''' herangezogen wird. '''NF''' ist in awk einen integrierte Variable die jeweils die Anzahl der Felder einer Zeile enthält. Die Schleife zählt also von 1 beginnend den Wert der Variabe '''i''' hoch, solange '''i''' kleiner oder gleich der Gesamtanzahl Anzahl der Felder der Zeile ist. Der Schleifenkörper (eingeschlossen in der inneren geschweiften Klammer) der dann jedesmal durchlaufen wird, enthält nur einen Befehl '''"print $i"'''. Dabei wird also bei jedem Durchlauf der Reihe nach jeweils ein Feld ausgegeben. Beim nächsten Durchlauf der Schleife das nächste usw. <br>
+
Diese Aktion besteht aus einer FOR-Schleife in der der Wert einer Variable '''i''' mit dem Ausgangswert von '''1''' hochgezählt wird und als Abbruchkriterium der Wert von '''NF''' herangezogen wird. '''NF''' ist in awk einen integrierte Variable die jeweils die Anzahl der Felder einer Zeile enthält. Die Schleife zählt also von 1 beginnend den Wert der Variabe '''i''' hoch, solange '''i''' kleiner oder gleich der Gesamtanzahl der Felder der Zeile ist. Der Schleifenkörper (eingeschlossen in der inneren geschweiften Klammer) der dann jedesmal durchlaufen wird, enthält nur einen Befehl '''"print $i"'''. Dabei wird also bei jedem Durchlauf der Reihe nach jeweils ein Feld ausgegeben. Beim nächsten Durchlauf der Schleife das nächste usw. <br>
Das gesamte awk Programm macht dabei also nichts anderes, als die Felder die sich in einer Zeile befinden jeweils auf einer seperaten Zeile wieder auszugeben.
+
Das gesamte awk Programm macht dabei also nichts anderes, als die Felder die sich in einer Zeile befinden jeweils auf einer separaten Zeile wieder auszugeben.
  
  
Zeile 230: Zeile 242:
 
=== Beispiel 4 ===
 
=== Beispiel 4 ===
 
  /sbin/lsmod | awk 'BEGIN {n = "no";} {if ($1 == "'"$module"'") n = "yes";} END {print n;}'
 
  /sbin/lsmod | awk 'BEGIN {n = "no";} {if ($1 == "'"$module"'") n = "yes";} END {print n;}'
Auch diese awk Programm kommt in der einen oder anderen Form öfter in Scripten vor.  
+
Auch diese awk Programm kommt in der einen oder anderen Form öfter in Scripten vor. <br>
 +
 
 +
 
 +
''('''Achtung:''' hier wird ein kleiner Tick eingesetzt, den man erst bei näherem Hinsehen erkennt:''<br>
 +
'''<font color="green">awk</font> ' <font color="red">BEGIN {n = "no";} {if ($1 == "</font> ' <font color="green">"$module"</font> ' <font color="red">") n = "yes";} END {print n;}</font> '  ''' <br>
 +
''Der Programmtext der für awk bestimmt ist, ist hier durch einfache Hochkommas unterbrochen, dadurch sieht die aufrufende Shell dazwischen die Shell-Variable und setzt so deren Wert beim Aufrufen des awk Programmtext entsprechend dem Inhalt der Shellvariable. )'' 
 +
 
 +
 
  
 
* als erstes haben wir hier eine '''BEGIN''' Bedingung. In der Aktion dazu wird eine Variable definiert und vorbelegt. '''n = "no"'''  
 
* als erstes haben wir hier eine '''BEGIN''' Bedingung. In der Aktion dazu wird eine Variable definiert und vorbelegt. '''n = "no"'''  
 
* Danach erfolgt der Programmablauf der da ohne weitere Bedingung auf jede einzelne Zeile die Aktion '''if ($1 == "'"$module"'") n = "yes"''' ausführt.
 
* Danach erfolgt der Programmablauf der da ohne weitere Bedingung auf jede einzelne Zeile die Aktion '''if ($1 == "'"$module"'") n = "yes"''' ausführt.
: Dabei wird auf eine Variable '''module''' aus dem aufrufenden Shellumgebung zurückgegriffen, ist der Wert des ersten Feldes gleich dem Inhalt dieser Shellvariable dann wird  '''n = "yes"''' ausgeführt.
+
: Dabei wird der Inhalt der Shell-Variable '''module''' , der beim Aufruf dort substituiert wird, mit dem ersten Feld verglichen. Ist er gleich, dann wird  '''n = "yes"''' ausgeführt.
 
* sind alle Zeile abgearbeitet dann startet die Bedingung '''END ''' ihre Aktion die darin besteht, den Wert der Variable '''n''' auszugeben
 
* sind alle Zeile abgearbeitet dann startet die Bedingung '''END ''' ihre Aktion die darin besteht, den Wert der Variable '''n''' auszugeben
 +
  
 
Die gesamte Befehlszeile in Verbindung mit der Shellvariable '''module''' gibt also entweder '''no''' oder '''yes''' als Ausgabe, je nachdem ob das entsprechende Modul dessen Name in der '''module''' Variable der shell hinterlegt ist, momentan im Kernel geladen ist oder nicht.
 
Die gesamte Befehlszeile in Verbindung mit der Shellvariable '''module''' gibt also entweder '''no''' oder '''yes''' als Ausgabe, je nachdem ob das entsprechende Modul dessen Name in der '''module''' Variable der shell hinterlegt ist, momentan im Kernel geladen ist oder nicht.
 +
  
 
   
 
   
Zeile 277: Zeile 298:
 
* die Variable '''sum_quant''' wird in jeder Zeile um den Wert von '''oquant''' erhöht
 
* die Variable '''sum_quant''' wird in jeder Zeile um den Wert von '''oquant''' erhöht
 
* zum Schluss erfolgt in jeder Zeile eine Ausgabe des Ergebnisses von '''sum_quant / nquant'''
 
* zum Schluss erfolgt in jeder Zeile eine Ausgabe des Ergebnisses von '''sum_quant / nquant'''
Script 2 rechnet also die Eingangswerte in eine andere Großenskala um, und gibt jeweils das aritmethische Mittel der bis hierhin durchlaufenen Werte aus.  
+
Script 2 setzt also die Eingangswerte mit einer Tabelle in andere Werte um, (vergleichbar vielleicht mit anderer nicht linear umrechenbaren Maßeinheit) und gibt jeweils das aritmethische Mittel der bis hierhin durchlaufenen Werte aus.  
 
Dieses wird dann über die Pipe weitergereicht
 
Dieses wird dann über die Pipe weitergereicht
  
Zeile 368: Zeile 389:
 
* [http://cm.bell-labs.com/cm/cs/awkbook/index.html The AWK Programming Language] das orginal Buch sollte wohl mittlerweile auch deutsch erhältlich sein
 
* [http://cm.bell-labs.com/cm/cs/awkbook/index.html The AWK Programming Language] das orginal Buch sollte wohl mittlerweile auch deutsch erhältlich sein
 
* ein unvollendetes [http://de.wikibooks.org/wiki/Awk awk-Wikibook]
 
* ein unvollendetes [http://de.wikibooks.org/wiki/Awk awk-Wikibook]
 +
 +
 +
-----
 +
 +
[[Konsole#Unixwerkzeuge|zurück zu UNIX-Werkzeuge]]
 +
[[Kategorie:Konsole]][[Kategorie:Scripte]]

Version vom 29. September 2008, 23:16 Uhr

AWK - eine kleine Einführung und Übersicht über ein mächtiges UNIX-Werkzeug


Was ist awk und was kann awk ?

Awk ist eine tratitionelle Script-Programmiersprache und auf jedem UNIX- und Linux-System zu finden. Der Interpreter ist ein Programm namens awk ( benannt nach den geistigen Urvätern Alfred v. Aho ; Peter J. Weinberger und Brian W. Kernighan ). Das gesamte Konzept ist für eine Batchverarbeitung ausgelegt, d.h ein awk Programm wird ohne äußeren Eingriff eines Benutzers von Anfang bis zum Ende ausgeführt. Häuptsächlich wird es zum Durchsuchen, Auswerten und Manipulieren von Daten eingesetzt.

awk wurde in den Haupzügen schon in den 70/80 Jahren des vorigen Jahrhunderts entwickelt und zählt zu den universellen UNIX-Standard-Werkzeugen. Es besitzt eine fest definierten Spachumfang und läßt sich nicht wie andere Progammierspachen fast beliebig erweitern. Dafür ist es jedoch sehr portabel und heute auf vielen Plattformen zu Hause. Obwohl heute Spachen wie Perl viele frühere Anwendungsbereiche von awk besser und schneller erledigen könnten, wird auch heute noch vielfach auf awk zurückgegriffen. Es gibt heute eine Reihe unterschiedlicher AWK-Implementierungen mit differierendem Leistungsumfang, auf Linux-Systemen ist das sehr leistungsfähige Gawk der Standard. Speziell kleinere awk-Programme lassen sich gut in Shell-Skripte einbetten, was häufig genutzt wird.


Das Grundprinzip von awk beruht auf der Verarbeitung einer Datei oder wenn nicht angegeben automatisch der Standardeingabe, dieser wird zeilenweise nach anwenderspezifischen Mustern durchsucht und beim Auffinden von Übereinstimmung eine dem jeweiligem Muster zugeordnete Aktion ausgeführt. Jeder Datensatz wird automatisch in Felder zerlegt, auf die man bequem zugreifen kann. Das Ergebnis wird in der Standardausgabe ausgegeben oder kann entsprechend umgeleitet werden. Die Eingabedatei wird nicht verändert. Sowohl Ein- wie auch Ausgabe wird dabei von awk als eine Folge von durch Zeilentrennzeichen separierte Zeilen (records) angesehen, die ihrerseits wieder durch Feldtrennzeichen in einzelne Felder aufgeteilt sind. Sowohl Zeilentrennzeichen (default ASCII-LF) als auch Feldtrennzeichen ( default Leerzeichen oder Tabulatorzeichen) können vom Anwender auch individuell angepasst werden. Eine Eingabezeile kann als Ganzes mit dem Bezeichner $0 angesprochen werden, die einzelnen Felder der Zeile mit $1 , $2 , $3 .... $n. Besonders geeignet sind somit Dateien in Listen- oder Tabellenform oder analog die Ausgaben von Befehlen in diesem Format, und davon gibt es auf jedem Rechner jede Menge.


Die Spachelemente von awk sind denen der Programmiersprache C nicht unähnlich. Die Möglichkeit von Konstanten und Variablen (auch als Feld und Array Elemente) die integierte Typenanpassung, die Steuerelementen zB in Form von Schleifen und Verzweigungen, frei definierbare und schon eingebauten Funktionen, Erweiterte Möglichkeiten der Formatierung und der Textmanipulation sowie die Integration einiger häufig benötigter mathematischer Funktionen, machen awk zu einem sehr mächtigem Tool. Mit seiner Hilfe lassen sich auf einfache Weise eine Vielzahl von Problemen lösen, für die es sonst wenig andere akzeptable Lösungsmöglichkeiten im Scriptumfeld gibt. Allerdings sollte man bedenken, awk ist eine interpretierende Sprache und desshalb nicht besonders schnell in der Abbarbeitung. Insbesondere bei der Bearbeitung großer Datenbestände in Punkto Geschwindigkeit ist awk gegenüber anderen kompilierbaren Sprachen und Tools im Nachteil. Für den einmaligen oder seltenen Gebrauch solcher Programme ist jedoch oftmals eine mögliche schnellere Programmentwicklung durch awk von Vorteil.


Kleine awk Lösungen lassen sich auf der Kommandozeile bequem mittels Pipe mit anderen Kommandos verbinden und so sowohl auf der Kommandozeile wie auch in Shellscripten sehr universell direkt benutzen. akw wird heute zB in der Systemverwaltung innerhalb von Scripten verwendet für die Bearbeitung von Konfigurationsdateien sowie für einmaliges oder gelegentliches komplexes Filtern, Konvertieren und Auswerten von umfangreichen Daten zB von Logdateien. Unterteilen kann man die Einsatzgebiete im Wesentlichen in 3 Gruppen.


  • Datenauswertung und -aufbereitung

Vor allem bei Anwendungen mit Listenausgabe, dabei sind uA. Filteroptionen und statistische Problemstellungen, wie z.B. Berechnung oder einfach nur Summierung von Feldinhalten oder Zählung von Häufigkeiten, in Verbindung mit weiteren Auswahlkriterien möglich


  • Datentransformation

Eingabedaten werden auf beliebige Weise neu gruppiert, neu formatiert oder selektiv verändert.


  • Datenvalidierung

Überprüfung der Daten auf ihre syntaktische und semantische Korrektheit. Auch komplettes Neuerstellen von Konfigurationsdateien aus vom User editierten Dateien.



Der grundlegende Befehlsaufruf

Für das Ausführens eines awk Programmes benötigen wir also einerseits den Interpreter, das ist das Programm namens awk selbst, weiterhin benötigen wir in den meisten Fällen die Datei oder den Datenstrom der bearbeitet werden soll und wir benötigen noch den awk-Programmtext. Prinzipell ist es möglich diesen Programmtext entweder in eine separaten Datei abzulegen und von dort aus darauf beim Programmaufruf zuzugreifen oder aber der Programmtext kann auch direkt auf der Komandozeile hinter dem Interpreteraufruf stehen.


awk -f Programmdatei Eingabedatei
Programmtext als separate Datei 

Dieser Aufruf wird meist bei umfangreichen oder universellen awk Programmen bevorzugt. Der Programmtext kann hierzu seperat in einer Datei entwickelt und genutzt werden, und muss nicht immer wieder neu auf der Komandozeile erstellt werden.


awk 'Programmtext' Eingabedatei
Programmtext vor Interpretation durch die Shell geschütz direkt hinter den Programmaufruf 

Diese Form ist besonders für klein und kleinste awk Programme geeignet. Im Falle eines Syntaxfehlers muss der gesamte awk Programmtext in der Komandozeile wieder neu erstellt oder eingegeben werden, das ist bei awk-Programmen die wesentlich länger als eine Zeile sind, selbst mit einer intelligenten Shell natürlich ungeschickt. Oftmals muss man auch auf hilfreiche übersichtliche Formatierung des awk Programmtextes verzichten, bei kleinsten Programmen erweist sich das nicht als Problem.


Beide Aufrufmöglichkeiten lassen sich in Shellscripten verwenden. Wobei oftmals innerhalb von Shellscripten die 2. Variante bevorzugt wird, da damit das awk Programm mit dem Shellscipt gleichzeitig entwickelt werden kann und hat dann auch beides als eine übersichtliche Einheit in einer einzigen Datei. Bei universalen awk Programmtexten, die eventuell auch von mehreren Scripten aufgerufen werden können, bietet sich jedoch die erste Variante besser an.



Die Programmstruktur

Ein awk Programm besteht aus einer Folge von Bedingungen und Aktionen die dieser Bedingung zugeordnet sind und hat folgende allgemeine Struktur

BEGIN       { Aktion... }
Bedingung_1 { Aktion... }
Bedingung_2 { Aktion... }
....
Bedingung_n { Aktion... }
END         { Aktion... }


  • BEGIN und END sind Schlüsselworte für spezielle Bedingungen innerhalb des Programmablauf, deren Aktionen werden entweder vor oder nach der Bearbeitung der Datei ausgeführt. BEGIN oder END Aktionen sind optional, also nicht jedes awk Programm muss sie beinhalten.
  • Um die Bedingungen von den Aktionen unterscheiden zu können, werden die Aktionen in geschweifte Klammern { } eingeschlossen.
  • Mehrere Aktionen können durch ; oder durch einen Zeilentrennzeichen getrennt werden.
  • Bedingungungen dürfen nicht über mehrere Zeilen geschrieben werden.
  • ein Aktion müssen in derselben Programmzeile beginnen, in der die Bedingung steht ( "{" reicht aus ) dürfen aber über mehrere Zeilen fortgesetzt werden.
  • bei einer fehlenden Bedingung, wird die Aktion für jede Zeile der Eingabedatei ausgeführt.
  • bei einer fehlenden Aktion, wird jede zur Bedingung passenden Zeile unverändert an die Ausgabe durchgereicht.



Der Programmablauf

  • Ist eine BEGIN Bedingung im Programm enthalten wird zuerst diese Aktion ausgeführt
hier ist es zB möglich spezielle Bedingungn (zB: Feldtrennzeichen für Ein- oder Ausgabe ) für den weiteren Programmablauf festzulegen, Variablen vorzubelegen oder Ausgaben zu machen, die dann in vor den verarbeiteten Daten in der Ausgabe erscheinen
  • Der eigentliche Hauptprogrammablauf erfolgt in einer Doppelschleife
dabei wird jeweils eine einzelne Zeile der Reihe nach auf alle programmierten Bedingungen geprüft und gegebenenfalls die dazugehörige Aktion ausgeführt. Sind alle Bedingungen für die erste Zeile abgearbeitet, wird die nächste Zeile geladen und diese wiederum auf alle Bedingungen geprüft, bis auch die letzte Zeile so abgearbeitet ist.
  • Ist eine END Bedingung enthalten, wird nach dem Durchlauf aller Zeilen die Aktion zur END Bedingung ausgeführt
Hier können jetzt die Variablen nach dem Programmdurchlauf verarbeitet und ausgegeben werden, oder Ausgaben erfolgen, die unterhalb der verarbeiteten Daten angehängt werden.



Die Bedingungen

Wie oben schon beschrieben, legt eine Bedingung fest, ob für eine Zeile die dazugehörige Aktion auszuführen ist oder nicht. In awk gibt es 2 prinzipielle Möglichkeiten für Bedingungen, einmal Reguläre Ausdrücke und zum anderem Vergleichsausdrücke. Reguläre Ausdrücke müssen in Schrägstriche /..../ eingeschlossen werden. Dabei kann sowohl auf den Inhalt der ganzen Zeile sowie auf deren einzelnen Felder wie auch auf die Variablen innerhalb des Programmes zugegriffen werden. Darüber hinaus ist es möglich mit logischen Verknüpfungen und Bereichen (analog etwa von - bis) zu arbeiten. Die Leistungsfähigkeit und Möglichkeiten von awk gehen hier weit über das hinaus, was andere Scriptsprachen, Tools und Programme können. An dieser Stelle nur ein paar einfache Beispiele.

/Kasten|Kiste/ {print $0}           # alle Zeilen die das "Kasten" oder "Kisten" enthalten
/^[1A]/        {print $0}           # alle Zeilen die mit '1' oder 'A' beginnen
/begin/,/ende/ {print $0}           # alle Zeilen vom ersten "begin" bis zum ersten "ende" 
$2 == "System" {print $0}           # alle Zeilen in denen das 2. Feld genau "System" ist
$2 ~ "System"  {print $0}           # alle Zeilen in denen im 2. Feld "System" enthalten ist
$2 !~ "System" {print $0}           # alle Zeilen in denen im 2. Feld nicht "System" enthalten ist
$3 >= $1+2     {print $0}           # alle Zeilen in denen Feld3 Größer oder gleich (Feld1 + 2) ist
NR==5 , NR==15 {print,$0}           # die Zeilen 5 bis 15
NR % 2 == 1    {print $0}           # alle ungeraden Zeilen
length > 40    {print $0}           # alle Zeilen die länger als 60 Zeichen sind
length($2) > 5 {print $0}           # alle Zeilen deren 2.Feld größer als 5 Zeichen ist
$2 !~ /[0-9]/  {print $0}           # alle Zeilen deren 2.Feld keine Ziffer enthält
$1 ~ /[0-9]/ && $3 !~ /t/ {print $0}# alle Zeilen wenn im Feld1 Ziffern und im Feld3 kein 't' enthalten



Die Aktion

Mit der Aktion werden im wesentlichen 2 Dinge getätigt, zum einem werden hier die Ausgaben erstellt, und zum anderen können hier die Variablen während des Programmablaufs geändert also zB abhängig von Werten innerhalb der Zeile neu berechnet werden. Eine Aktion kann ihrerseits wieder mit Hilfe von Bedingungen oder Schleifen gesteuert werden.
Die Ausgabe ist die wesentliche Aufgabe der Aktionen, in jedem awk Programm gibt es mindesdens eine Ausgabeaktion, denn ohne eine einzige Ausgabe würde unser awk Programm ja gar nichts machen. Die einfachste Form der Ausgabe ist die Funktion print darüber hinaus sind aber weitere sehr leistungsfähige Printfunktionen analog der Programmiersprache C vorhanden. An dieser Stelle nur einige einfache Ausgabe-Beispiele, mit den entsprechenden Ausgaben.

 
LINUX:/tmp # echo "1 2 3 4 5" | awk '{print $0}'          
1 2 3 4 5
LINUX:/tmp # echo "1 2 3 4 5" | awk '{print $1,$3,$5}'
1 3 5
LINUX:/tmp # echo "1 2 3 4 5" | awk '{OFS=":";print $1,$3,$5}'
1:3:5
LINUX:/tmp # echo "1 2 3 4 5" | awk '{print $1 $3 $5}'
135
LINUX:/tmp # echo "1 2 3 4 5" | awk '{print "Wert3=",$3}'
Wert3= 3
priv0001:/tmp # echo "1 2 3 4 5" | awk  '{if ($2<3) print $2 ; else print $5}'
2
LINUX:/tmp # echo "1 2 3 4 5" | awk  '{print $2, $1+5, $3*$4}'
2 6 12
LINUX:/tmp # echo "1 2 3 4 5" | awk  '{sum=0;for (i=1;i<=NF;i++)sum+=$i; print sum }'
15
LINUX:/tmp # echo "1 2 3 4 5" | awk  '{printf "Ergebnis : %2.7f \n", $5 / $3  }'
Ergebnis : 1,6666667
LINUX:/tmp # echo "10 34 4 22 6" | awk '{for (i=1;i<=NF;i++){j=1;while(j<=$i){printf "%s","*";j++};print "";}}'
**********
**********************************
****
**********************
******

eine kleine Spielerei noch zum Abschluss nur um zu verdeutlichen was mit wenigen Aktionen mit awk machbar ist.

awk '{for (i=1;i<=NF;i++)anzahl[$i]++} END {for (i in anzahl) print anzahl[i],i}' buch.txt 

würde die einzelnen Worte der Datei buch.txt erfassen, statistisch zählen und im Format "Anzahl Wort" ausgeben. Ein sort -n könnte uns das dann anschließend noch sortieren



Einige einfache Beispiele mit Erläuterungen

In den weiten des WWW gibt es genügend großartige awk-Beispiele die einzelne Funktionen sehr anschaulich oftmals sogar spektakulär belegen oder sogar die volle Leistungsfähigkeit und Genialität von awk an Hand von komplexen Programmen verdeutlichen. Jedoch oftmals sind für die Praxis wenig wirklich wiederverwendbare Elemente enthalten oder gehen in der Fülle der Informationen unter. Aus diesem Grund sehen wir uns doch einmal auf unseren Rechnern um. In vielen Scripten auf unseren Rechnern werden wir awk Befehle finden, die meisten davon sind wohl Einzeiler oder kleine kompakte Programme, genau das richtige für den Anfang. Picken wir uns doch einmal einige heraus und untersuchen sie etwas genauer.


Beispiel 1

BEFEHL | awk '{print $3}' | BEFEHL

Diesem sehr einfachen awk Befehlskonstrukt werden wir in vielen Scipten wiederfinden. Sie unterscheiden sich nur in der Zahl hinter dem $.
Die Ausgabe eines Befehles wird mittels Pipe an awk übergeben. Die einfachen Hochkomma dienen hier nur dem Schutz vor der Interpretation der Shell so das unser ganzer awk Programmtext nur aus {print $3} besteht. Die Ausgabe wird anschließend wiederum an den nächsten Befehl mittels Pipe weitergereicht.

{print $3} wir haben hier nur eine Aktion ohne eine einzige Bedingung, sie wird also mit jeder Zeile der Eingabedatei durchgeführt. Die Aktion bedeutet hier im Beispiel einfach nur "gebe für diese Zeile das Feld Nr. 3 zu Ausgabe". Also wir schneiden von jeder Zeile alles weg, außer Feld Nr. 3. Warum nimmt man dann nicht den Befehl cut mit dem geht doch das auch ?

Die Antwort ist sehr einfach, cut kennt nur jeweils einen einzigen Feldtrenner, awk arbeitet hier mit mehr Feldtrennern und geht damit viel intelligenter um als die sturen Regeln in cut. Wir wissen nicht was ein User beim editieren einer Datei zwischen die einzelnen Felder für Feldtrenner einbaut, ein Leerzeichen? mehrere Leerzeichen? ein Tab-Zeichen oder mehrere? oder gar Tab- und Leerzeichen gemischt? Für den User zählt hier einzig das äußere Erscheinungsbild der Datei, bei cut müssten wir hier ganz schön vorarbeiten, damit wir auch sicher genau das richtige Feld erwischen, egal was der User eingegeben hat. awk macht das im Schlaf, ein direkter Vergleich zwischen awk und cut soll das belegen.

# echo -e "1  2 3\t4 \t \t 5" | awk '{print $1,$2,$3,$4,$5}'
1 2 3 4 5
# echo -e "1  2 3\t4 \t \t 5" | cut -f 1-5
1  2 3  4                5
# echo -e "1  2 3\t4 \t \t 5" | cut -f 5

# echo -e "1  2 3\t4 \t \t 5" | cut -f 4
 5
# echo -e "1  2 3\t4 \t \t 5" | cut -f 3

# echo -e "1  2 3\t4 \t \t 5" | cut -f 2
4



Variationen

BEFEHL | awk '" " { print $2 }' | BEFEHL

hier handelt es sich um genau das selbe, nur wird hier zusätzlich noch einmal deutlich gemacht, dass hier eine leere, also keine Bedingung steht.

BEFEHL | awk -F" " '{ print $8}' | BEFEHL
BEFEHL | awk -F[ '{print $1}' | BEFEHL
BEFEHL | awk -F. '{print $1 , $2 , $3}' | BEFEHL

hier wird mit Hilfe der Option -F ein spezieller Feldtrenner für die Eingabedaten festgelegt. Im letzten Befehl wird nicht nur ein Feld ausgegeben sondern die Felder 1 2 und 3 und als Feldtrennezeichen der Ausgabe wird das Standardzeichen " " von awk verwendet. ( Achtung wird hier {print $1 $2 $3} verwendet, dann werden die Felder ohne Trennzeichen ausgegeben) siehe auch obrige Beispiele

VARIABLE=`BEFEHL | awk '{print $1 * 60}'`
VARIABLE=`BEFEHL | awk -F: '{print $1 / $2 * $3 + 1}'`

hier wird mit den Feldern vor der Ausgabe gerechnet im ersten Fall wird der 60fache Wert von Feld 1
und im zweiten Fall der Wert von Feld1 / Feld2 * Feld3 + 1 errechnet und ausgegeben und somit in der Variable gepeichert

VARIABLE=`BEFEHL | awk '{ORS=""; print $1}'`

hierbei handelt es sich um eine Anpassung des Ausgabeformates, die implementierte Variable ORS in awk beinhaltet das Zeilentrennzeichen der Ausgabe, also normalerweise '\n'. In diesem Falle wurde es geleert, das bedeutet in der Ausgabe dieser Aktion gibt es keinen Zeilenvorschub, die nächste Aktion würde direkt an das Ende der Ausgabe weiterschreiben.

BEFEHL | awk '{OFS=";"; print $1,$3,$8}'

hierbei handelt es sich ebenfalls um eine Anpassung des Ausgabeformates, nur wird hier die implementierte Variable OFS geändert, in ihr steht das Feldtrennzeichen der Ausgabe, also normalerweise ein Leerzeichen. Hier werden also für jede Zeile der Eingabe die Felder 1 3 und 8 mit einem ; getrennt ausgegeben.


Beispiel 2

Befehl | awk '/Video:/ {print $4}' | BEFEHL

hier haben wir jetzt neben der schon bekannten einfachen Aktion noch eine Bedingung. /Video:/
die Schrägstriche zeigen uns es handelt sich um einen Regulären Ausdruck. Damit kommen hier für die Aktion nur die Zeilen in Betracht die irgendwo innerhalb der Zeile den Sting "Video:" enthalten. Wir haben hier also vergleichbar den Ausdruck | grep "Video:" | cut -f 4 | implementiert


Variationen

BEFEHL | awk -F= '/quant=/ {print $2}' | BEFEHL 

das kennen wir schon, es wurde nur zusätzlich noch ein anderes Feldtrennzeichen für die Eingabe definiert

BEFEHL | awk '/unit/ {print sum+=$2}' | BEFEHL

Diese Variante hat hier eine Neuerung für uns, es wird hier nicht der Inhalt eines Feldes selbst ausgegeben, sondern der Inhalt einer benutzerdefinierten Variable. sum
Die Variable sum hat beim Programmstart den Wert 0 (weil nicht anders initalisiert)
Mit jeder Zeile in der der Suchstring "unit" enthalten ist, wird zu diesem Wert jeweils der Wert von Feld2 dazuaddiert und dann diese Summe ausgegeben.
Bei mehreren gefundenen Zeilen mit "unit" wird unser awk Programm also eine entsprechende Zahlenreihe ausgeben, die jeweils um den Wert des Feldes2 der betreffenden Zeilen ansteigt.

$hdparm -d /dev/dvd | awk '$1 == "using_dma" {print $3}' 

hier handelt es sich um das selbe Prinzip, nur wurde hier als Bedingung kein Regulärer Ausdruck verwendet, sondern ein Vergleichsausdruck, es wird hier geprüft ob der Inhalt von Feld 1 gleich "using_dma" entspricht, und nur in diesem Fall wird das Feld3 ausgegeben.



Beispiel 3

BEFEHL | awk '{for (i=1;i<=NF;i++){print $i}}' | BEFEHL

Dieser oder ein ähnlicher awk Befehl wird in einigen Scripten verwendet. Es gibt keine Bedingung also wird für jede Zeile die Aktion gestartet. ( Die Schreibweise der for-Schleife zeigt hier auf den ersten Blick schon die Verwandschaft von C ).

Diese Aktion besteht aus einer FOR-Schleife in der der Wert einer Variable i mit dem Ausgangswert von 1 hochgezählt wird und als Abbruchkriterium der Wert von NF herangezogen wird. NF ist in awk einen integrierte Variable die jeweils die Anzahl der Felder einer Zeile enthält. Die Schleife zählt also von 1 beginnend den Wert der Variabe i hoch, solange i kleiner oder gleich der Gesamtanzahl der Felder der Zeile ist. Der Schleifenkörper (eingeschlossen in der inneren geschweiften Klammer) der dann jedesmal durchlaufen wird, enthält nur einen Befehl "print $i". Dabei wird also bei jedem Durchlauf der Reihe nach jeweils ein Feld ausgegeben. Beim nächsten Durchlauf der Schleife das nächste usw.
Das gesamte awk Programm macht dabei also nichts anderes, als die Felder die sich in einer Zeile befinden jeweils auf einer separaten Zeile wieder auszugeben.



Beispiel 4

/sbin/lsmod | awk 'BEGIN {n = "no";} {if ($1 == "'"$module"'") n = "yes";} END {print n;}'

Auch diese awk Programm kommt in der einen oder anderen Form öfter in Scripten vor.


(Achtung: hier wird ein kleiner Tick eingesetzt, den man erst bei näherem Hinsehen erkennt:
awk ' BEGIN {n = "no";} {if ($1 == " ' "$module" ' ") n = "yes";} END {print n;} '
Der Programmtext der für awk bestimmt ist, ist hier durch einfache Hochkommas unterbrochen, dadurch sieht die aufrufende Shell dazwischen die Shell-Variable und setzt so deren Wert beim Aufrufen des awk Programmtext entsprechend dem Inhalt der Shellvariable. )


  • als erstes haben wir hier eine BEGIN Bedingung. In der Aktion dazu wird eine Variable definiert und vorbelegt. n = "no"
  • Danach erfolgt der Programmablauf der da ohne weitere Bedingung auf jede einzelne Zeile die Aktion if ($1 == "'"$module"'") n = "yes" ausführt.
Dabei wird der Inhalt der Shell-Variable module , der beim Aufruf dort substituiert wird, mit dem ersten Feld verglichen. Ist er gleich, dann wird n = "yes" ausgeführt.
  • sind alle Zeile abgearbeitet dann startet die Bedingung END ihre Aktion die darin besteht, den Wert der Variable n auszugeben


Die gesamte Befehlszeile in Verbindung mit der Shellvariable module gibt also entweder no oder yes als Ausgabe, je nachdem ob das entsprechende Modul dessen Name in der module Variable der shell hinterlegt ist, momentan im Kernel geladen ist oder nicht.



Beispiel 5

Sehr oft aber durchaus nicht immer sind die awk Programme so kurz und leicht zu verstehen wie in obrigen Beispielen
folgender Ausschnitt wurde gefunden in /usr/bin/anytovcd.sh

echo "`awk -F= '/quant=/ {print $2}' "$1" | \
    awk '{iquant=$1; \
    oquant=iquant; \
    if (iquant>=  10) {oquant=9}; \
    if (iquant>=  12) {oquant=10}; \
    if (iquant>=  14) {oquant=11}; \
    ....
    ....
    if (iquant>=  88) {oquant=28}; \
    if (iquant>=  96) {oquant=29}; \
    if (iquant>= 104) {oquant=30}; \
    if (iquant>= 112) {oquant=31}; \
    nquant+=1; \
    sum_quant+=oquant; \
    print sum_quant/nquant}' | \
    tail -1 | awk -F. '{print $1}'`"

hier handelt es sich sogar um 3 awk Programme in einer Befehlskette. Das erste Script

awk -F= '/quant=/ {print $2}' "$1"

sucht in der Datei die dem Script oder der Funktion als erster Parameter übergeben wurde nach dem Vorkommen von "quant=" und gibt mit dem Feldtrennzeichen "=" dann jeweils das 2. Feld an die Ausgabe.


über einen Pipe wird diese Ausgabe an das 2. awk Programm übergeben, da es hier keine Bedingung gibt, wird mit jeder Zeile folgende Aktionen der Reihe nach abgearbeitet.

  • Die Variabel iquant wird mit dem Inhalt von Feld1 belegt. (Wir wissen aus Script 1, es gibt nur diese erste Feld in jeder Zeile)
  • Die Variable oquant bekommt den selben Wert wie iquant
  • in einer langen Liste (unterscheiden sich nur durch ihre Werte) wird anschließend iquant mit Zahlen verglichen und entsprechend oquant auf einen anderen Wert gesetzt
  • ist diese Liste abgearbeitet hat also oquant einen anderen aber einen von der Größe von iquant abhängigen Wert
  • die Variable nquant wird in jeder Zeile jeweils um 1 erhöht
  • die Variable sum_quant wird in jeder Zeile um den Wert von oquant erhöht
  • zum Schluss erfolgt in jeder Zeile eine Ausgabe des Ergebnisses von sum_quant / nquant

Script 2 setzt also die Eingangswerte mit einer Tabelle in andere Werte um, (vergleichbar vielleicht mit anderer nicht linear umrechenbaren Maßeinheit) und gibt jeweils das aritmethische Mittel der bis hierhin durchlaufenen Werte aus. Dieses wird dann über die Pipe weitergereicht


tail -1

läßt hier nur die letzte Zeile passieren und übergibt diese wiederum per Pipe an das 3. awk-Script


awk -F. '{print $1}'

arbeitet mit dem Feldtrenner Punkt und gibt mit dem ersten Feld desshalb nur den Ganzzahligen Wert des ihm ubergebenen Wertes aus (Kommastellen werden abgeschnitten).



Beispiel 6

Wer bis hier her einigermaßen verstanden hat wie awk funktioniert, wird mit folgendem kein Problem haben
gefunden in /usr/bin/fontprop.sh

awk -F- '
{
    printf "FONTNAME_REGISTRY \"%s\"\n", $1;
    printf "FOUNDRY \"%s\"\n", $2;
    printf "FAMILY_NAME \"%s\"\n", $3;
    printf "WEIGHT_NAME \"%s\"\n", $4;
    printf "SLANT \"%s\"\n", $5;
    printf "SETWIDTH_NAME \"%s\"\n", $6;
    printf "ADD_STYLE_NAME \"%s\"\n", $7;
    printf "PIXEL_SIZE %d\n", $8;
    printf "POINT_SIZE %d\n", $9;
    printf "RESOLUTION_X %d\n", $10;
    printf "RESOLUTION_Y %d\n", $11;
    printf "SPACING \"%s\"\n", $12;
    printf "AVERAGE_WIDTH %d\n", $13;
    printf "CHARSET_REGISTRY \"%s\"\n", $14;
    printf "CHARSET_ENCODING \"%s\"\n", $15;
}' $*

Dem awk Befehl werden hier eine oder mehrere Dateien übergeben die jeweils eine oder mehrere Zeilen mit jeweils fest definierten Spalteninhalten haben. Die einzelnen Spalten sind durch Bindestrich "-" getrennt. (eine genaue Aufschlüsselung befindet sich auch im Script)

awk setzt diese Tabelle mit wagerechten Infomationen in eine vom Menschen gut verständliche zeilenweise Auflistung mit vorangesteller Feldbenennung um. Benutzt wird hierbei die printf Funktion, die wie in C funktioniert.



Beispiel 7

Wer nun der Meinung ist, awk etwas zu kennen, darf sich jetzt auch etwas komplizierteren aussehenden awk Konstrukten auf seinem eigenem Rechner nähern.
gefunden in der /etc/rc.d/ntp

if [ -r $NTP_CONF ] ; then
        cat $NTP_CONF | awk -v MAX_AUTO=$MAX_AUTO '
            /^[[:space:]]*server[[:space:]]+127.127/ {
                next
            }
            /^[[:space:]]*(server|peer)[[:space:]]/ {
                if ( MAX_AUTO ) {
                    printf " %s", $2
                    if ( --MAX_AUTO == 0 )
                        exit 0
                }
            }
        '
    fi
  • gestartet wird hier awk mit der Option "-v MAX_AUTO=$MAX_AUTO" Damit wird für awk eine Variable MAX_AUTO inititiert und mit den Wert der gleichnamingen Variable aus der Shellumgebung vorbelegt.
  • bei der weiteren Analyse des Programmes wird man dieses Mal 2 Bedingungen finden mit jeweils der dazugehörigen Aktion
  • Beide Bedingungen stehen in /.../ es handelt sich also um Reguläre Ausdrücke. ( die genaue Bedeutung dieser Regulären Ausdrücke ist für die weiter Untersuchung erst einmal nebensächlich.
  • der zur ersten Bedingung gehörende Aktionsblock enthält nur das Schlüsselwort next, next gehört zu den gezielten Abbruchbedingungen von awk und bedeutet, das diese Zeile nicht mehr mit weiteren folgenden Bedingungen geprüft werden soll, sondern sofort mit der nächste Zeile und der ersten Bedingung weiter gemacht wird. Beim Zutreffen der ersten Bedingung in unserem Programm wird also die 2. Bedingung gar nicht erst geprüft, sondern gleich mit der nächsten Zeile weitergemacht.
  • der zur zweiten Bedingung gehörende Aktionsblock wird erst richtig erkennbar wenn wir ihn etwas umformatieren.
if ( MAX_AUTO ) { printf " %s", $2 
                  if ( --MAX_AUTO == 0 ) exit 0
                }
  • entsprechend dem Wert der Variable MAX_AUTO wird eine printf Aktion ausgeführt und zwar dann, wenn MAX_AUTO ungleich 0 ist wird das Feld 2 formatiert ausgegeben
  • im selben Programmblock befindet sich nach der printf Funktion noch eine weitere Aktion die danach ausgeführt wird. Dabei wird die Variable MAX_AUTO mit einem pre-Dekremen-Operator -- um eins verringert und anschließend geprüft ob sie jetzt 0 ist, wenn ja wird "exit 0" ausgeführt,( exit ist eine weitere Abbruchbedingung von awk und diese beendet die gesamte Abbarbeitung des Scriptes an dieser Stelle)

Das gesamte Programm kann also maximal soviele Ausgaben machen wie ihm per Variable MAX_AUTO übergeben werden.



weiterführende Links



zurück zu UNIX-Werkzeuge