Awk: Grundlagen: Objektorientierte Programmierung

Aus Wikibooks


Einleitung[Bearbeiten]

AWK besitzt selbst keine Sprachmittel für die objektorientierte Programmierung. Trotzdem ist eine objektorientierte Programmierung in AWK in Ansätzen möglich. Dabei sind aber die Besonderheiten von AWK zu beachten und zu nutzen.

Sinn macht ein objektorientierter Ansatz immer wenn sich abzeichnet, dass ein Programm doch etwas größer und komplexer wird, oder die Wiederverwertbarkeit von Code verbessert werden soll.

Pakete/Biblotheken[Bearbeiten]

Um Code leichter wieder verwerten zu können, ist es notwendig, Dateien in andere Dateien als Bibliotheken einbinden zu können. Die Funktionalität bietet AWK von Hause aus nicht. Aber auf fast allen Systemen gibt es einen kleinen Wrapper namens igawk der quasi als Linker fungiert. igawk durchsucht das Skript nach dem Schlüsselwort @include und kopiert automatisch die Dateiinhalte der dahinter angegeben Datei ein.

#!/usr/bin/igawk -f

@include ./functions.awk

Hier wird beispielsweise der Inhalt von der Datei functions.awk eingelesen. Alternativ kann man dem AWK-Interpreter die Libs auch als Parameter übergeben. Siehe hier zu Abschnitt: "Funktionsbibliotheken". Wenn man den Flag "-W lint" verwenden will, ist diese Methode sinnvoller, weil man dann brauchbarere Meldungen bekommt.

Namensräume[Bearbeiten]

AWK hat keine Namensräume. Um trotzdem nicht in Konflikte zu geraten, kann man sich mit Konventionen behelfen um sozusagen Seminamensräume zu haben. So wird es zum Beispiel Sinn machen, wenn man sehr viele Funktionen über @include einbindet, diese mit Namensräumen voneinander abzugrenzen. Um das zu erreichen könnte man sich darauf verständigen, Funktionsnamen - getrennt durch Unterstrich - Namensräume voran zu stellen. Beispiel:

#!/usr/bin/igawk -f

@include ./net_lib.awk
@include ./store_lib.awk

net_getURlIP("myWesite.de", _ip)
net_getRSS(_ip, _feedNews)
store_addNews(_feedNews)

Auf diese Weise ist zum einen sehr gut zu sehen, zu welcher Lib die Funktion gehört und zum anderen, können zwei unterschiedliche Libs die gleichen Funktionsnamen benutzen. Zum Beispiel:

#!/usr/bin/igawk -f

@include ./net_lib.awk
@include ./sql_lib.awk

net_Request("http://www.myserver.de/index.html")
sql_Request("SELECT * FROM mytable")

Objekte[Bearbeiten]

Fundamentales Element der objektorientierten Programmierung sind natürlich Objekte. Auf Grund dessen das AWK komplexe Typen hat die zudem noch als Referenzen und nicht als Kopien an Funktionen übergeben werden, ist hier eine gute Basis gegeben. Wer noch nicht den Absatz Arrays als Parameter gelesen hat, sollte das jetzt an dieser Stelle nachholen! Alles Weitere baut darauf auf. Als Objekt(-Instanzen) werden uns associative array, also assoziative Arrays dienen. Hier ein Beispiel:

auto["raeder"] = 4
auto["kennzeichen"] = "awk - 33345"
auto["eigentuemer"] = "Peter Mustermann"

Konstruktoren[Bearbeiten]

Die von uns verwendeten assoziativen Arrays haben natürlich nur Attribute und keine Methoden, deshalb auch keine Konstruktoren. Wir behelfen uns deshalb mit Fabrikmethoden, im Englischen Factory genannt:

FactoryCar(auto)
print "Das Auto hat " auto["raeder"] " Räder!"

Die Factory-Methode könnte so aussehen:

function FactoryCar(_auto)
{
   _auto["raeder"] = 4
   _auto["kennzeichen"] = ""
   _auto["eigentuemer"] = ""
}

Der vorangestellte Unterstrich bei dem übergebenen Array ist eine Konvention, um anzuzeigen, dass es sich um eine lokale Variable handelt.

Vererbung[Bearbeiten]

Mit den Factory-Methoden ist jetzt so etwas wie eine Vererbung möglich:

function FactoryTruck(_auto)
{
   FactoryCar(_auto)
   _auto["raeder"] = 6
}

Zur Erstellung des Objekts Truck wird die Funktionalität von der Klasse Car bzw. der Factory-Methode FactoryCar() benutzt. In der Factory-Methode FactoryTruck() werden nur noch die Erweiterungen ergänzt. Die gemeinsamen Attribute Kennzeichen und Eigentümer werden durch denselben Code generiert.

Ausnahmen[Bearbeiten]

Ein weiteres Element der objektorientierten Programmierung ist die Ausnahmebehandlung (engl. exception handling). Mit ein bisschen Tricksen bekommt man aber auch das hin in AWK. Am besten lässt sich das durch ein Beispiel zeigen. Uns zwar mit dem Klassiker 99-bottles-of-beer.net/ :

   #! /usr/bin/awk

   BEGIN {
       # Setup
       TRUE = 1
       FactoryWall(_wall)
       startLoop(_wall)
   }

   function startLoop(_wall, _ex)
   {
       while ( TRUE )
       {
           print "------------------------------------------------"
           _re = singingVerse(_wall)
           if ( ExceptionHandler(_re,_ex) )
           {
               print _ex["message"] ","
               print _ex["message"] "."
               if(_ex["specification"] == "SingleBottleException")
               {
                   continue
               } else if (_ex["specification"] == "ZeroBottleException")
               {
                   print "Go to the store and buy some more, "
                   print "99 bottles of beer on the wall."
                   print "================================================"
                   break
               }
           } 
       }
   }

   function singingVerse(_wall)
   {
       if(_wall["bottles"] == 1)
       {
           _wall["bottles"] = _wall["bottles"] - 1
           return "Exception@SingleBottleException@1 bottle of beer on the wall"
       } else if (_wall["bottles"] == 0)
       {
           return "Exception@ZeroBottleException@No more bottles of beer on the wall"
       }
       print _wall["bottles"] " bottles of beer on the wall,"
       print _wall["bottles"] " bottles of beer on the wall."
       _wall["bottles"] = _wall["bottles"] - 1
       return 1
   }

   function FactoryWall(_wall)
   {
       _wall["typ"] = "Wall"
       _wall["bottles"] = 99
   }

   function ExceptionHandler(_info,_ex, _array)
   {
       if( index(_info, "Exception@") != 0)
       {
           split(_info, _array, "@")
           _ex["specification"] = _array[2]
           _ex["message"] = _array[3]
           return 1
       }
       return 0
   }

Die Funktion singingVerse() gibt entweder 1 (also true) zurück oder wirft eine Ausnahme. Die Funktion ExceptionHandler() kann erkennen ob eine Ausnahme geworfen wurde, was sie mit der Rückgabe von 1 und 0 (true und false) tut. Darüber hinaus stellt sie über eine definierte Schnittstelle die Informationen zur Verfügung, die es für eine Ausnahmebehandlung braucht. Und zwar wieder in der Form eines Objektes, bzw. eines assoziative Arrays. Der besitzt die Attribute specification mit der Art der Ausnahme und message wo die eigentliche Fehlermeldung gespeichert ist.

Weiterführendes[Bearbeiten]

Inspiriert ist dieser geschilderte Ansatz von den Konzepten der Softwarebibliothek und Framework GObject, das objektorientiertes Programmieren in der Programmiersprache C ermöglicht.