← Journal · Archiv

awk-Tutorial Teil 2

October 24, 2005

Ab und an bekommt man als Programmierer Daten aus zwei Quellen, die miteinander verschmolzen werden müssen. Das gehört eigentlich zu den Standardaufgaben in meinem Job. awk ist bei solchen Arbeiten sehr flexibel und nimmt mir dadurch viel Arbeit ab.
Oft bekommt man die CSV-Daten auch in unterschiedlicher Qualität und Format. Denn CSV (Commata Seperated Values) ist nicht gleich CSV. Im ersten Teil meines Mini-Tutorials, hatten wir eine Tabulator getrennte CSV (ab und zu auch als TSV bezeichnet).
Üblich sind neben Tabulatoren auch Semikolon. Ich favorisiere zwei aufeinander folgende Pipes (||), da sie wirklich sehr selten in Text vorkommen, eignen sie sich bestens.
Da wir schon gelernt haben wie man den Field-Seperator in awk ändern kann, sind die unterschiedlichen Formate kein Problem. Diesmal will ich als Beispiel die Produktdatenbank des Copyleftstores (Werbung, Werbung), Froogle-Tauglich machen. Den Produkten in einer Textdatei möchte ich den Link zu ihren Bildern und ihren Produktseiten, aus einer anderen Textdatei, hinzufügen.
Um zwei Quellen miteinander zu mergen bedarf es etwas ausführlicherem Code und den Gebrauch der BEGIN- und END-Klauseln, die ich ja bereits im ersten Teil vorgestellt habe.
In der BEGIN-Klausel laden wir den Produktkatalog den wir in der Variable source definiert haben.

BEGIN {
  #Artikelstamm laden

  #Abbruch wenn Quellenangabe fehlt
  if (source == "") {
    print "Es ist keine Quelle angegeben";
    exit;
  }

  #Erste Zeile aus Quelle laden
  eof = (getline line < source );

  #Zeile für Zeile Datei durchlaufen
  while (eof > 0) {

    #Input aus Datei aufsplitten in temporäres Array
    split(line, product, “\t”);

    #Array-Index inkrementieren
    catalog_count++;

    #Array füllen
    catalog[catalog_count, 1] = product[1]; #offer_id
    catalog[catalog_count, 2] = product[2]; #name
    catalog[catalog_count, 3] = product[3]; #description
    catalog[catalog_count, 4] = product[4]; #price
    catalog[catalog_count, 5] = product[5]; #category
    #Wenn kein Bildchen gefunden wurde, dann wird_
eben das Standardbildchen der Kategorie verwendet.
    catalog[catalog_count, 6] = “http://copyleftstore.de/images/”_
tolower(product[5]) “.png”;

    #Nächste Zeile aus Quelle laden
    eof = (getline line < source );
   }

  #Quelle schließen
  close(source)  ;

  #Für nächste Klausel Field-Seperator festlegen.
  FS = "\|\|";
}

Wir haben nun den Inhalt aus source in eine Array gepackt. In der nächsten Klausel laden wir Zeile für Zeile die Bildinformationen aus images.txt ein und vergleichen in einer For-Schleife ob wir im Array das passende Produkt dazu finden. Man kann natürlich auch erst die Bilder laden und dann die Produkte.

{
  #Images in Array nach und nach einpflegen

  #offer_id||image_url

  for (i = 1; i <= catalog_count; i++) {
    if (toupper($1) == toupper(catalog[i, 1])) {
      catalog[i, 6] = $2;
      break;
    }
  }
}

Zum Schluss geben wir in STDOUT das aufbereitete Array komplett mit Header aus.

END {
  print "offer_id\tname\tdescription\tprice\tcategory\t_
image_url\tproduct_url";

  for (i = 1; i <= catalog_count; i++) {
    printf "%s\t%s\t%s\t%s\t%s\t%s\thttp://copyleftstore.de/_
product_info.php?language=de&products_id=%s\n",
    catalog[i, 1],
    catalog[i, 2],
    catalog[i, 3],
    catalog[i, 4],
    catalog[i, 5],
    catalog[i, 6],
    catalog[i, 1];
  }
}

Das ganze starten wir mit folgender Befehlszeile. Sie enthält mit dem Optionsschalter -v die Variable source.

awk -v source=produktkatalog.txt -f froogle.awk_
images.txt > copyleftstore.txt

Fertig, das Endergebnis kann ich nun bei Froogle hoch laden.
Die Kür wäre nun den Code so zu optimieren, dass die kleine For-Schleife nur noch die Zeilen durchläuft, die noch kein Bild zugeordnet bekamen. Das wäre mit der direkten Ausgabe in STDOUT möglich. Bereits zugeordnete Produkte würden aus dem Array gelöscht werden, wodurch die For-Schleife zum Ende hin immer schneller werden würde. In END würden dann nur noch die Artikel ausgegeben werden, die kein Bild gefunden haben. Der Vorteil liegt auf der Hand, bei einer Produktpalette von gerade einmal 41 Produkten reicht die Performance. Bei 410.000 sieht das ganze dann schon anders aus.

5 Kommentare

Peter ·

OK, das wird wunderbar funktionieren. Ich dagegen benutze für Schnittstellen (dein Fall ist ja quasi nichts Anderes) stets meinen heißgeliebten SQL-Server (in meinem Fall der von Microsoft). Über DTS und gespeicherte Prozeduren mit T-SQL kann man das wunderbar lösen. Für meinen Geschmack sogar wesentlich “anschaulicher” (joins).
Wenn man bedenkt, dass in der nächsten Generation des SQL-Servers sogar VB (.NET, weiß nicht genau) in gespeicherten Prozeduren verwendet werden kann, wird die Sache noch interessanter.

lemming ·

Ja der DTS ist vielerlei hinsicht gut. Ohne ihn wäre es ja eigentlich gar nicht möglich mit dem MS-SQL Server zu arbyten, ohne gleich Programmierer zu sein.

Peter ·

Naja… etwas übertrieben… Einen bulk insert kriegt auch ein Nicht-Programmierer hin. Trotzdem sind die DTS unschlagbar bei der Schnittstellenprogrammierung.

Tina ·

Hi,

gut das es leute gibt die sich noch die mühe machen und für solche programme oder scripts tuts schreiben.

ich beschäftige mich auch gerade mit awk und stehe aber erst am anfang mich mit programmiersprachen und scripten zu befassen.

das obige tutorial funktioniert bei mir nicht.
ich habe alles kopiert und in der froogle.awk abgespeichert.
das einzige was ausgegeben wird ist
“code”
offer_id name description price category image_url product_url
“/code”

ich finde auch das du dein tutorial viel zu wenig dokumentiert hast und als 2tes zu schwierig ist.

ein einsteiger wie ich wird nach dem lesen deines ersten tutorials, das 2te nicht verstehen können, da es ja doch schon richtig ans eingemachte geht.

ich habe schon sämtliche seiten im web die awk beschreiben gelesen und habe auch schon einige einfache dinge verwirklichen können.

vielleicht hast du ja noch ein paar tips für mich awk zu erlernen oder zu verstehen.

schön währe evtl. ein tutorial teil 1.5 um anschliessend das 2te verstehen zu können.

dennoch, big thx for tutorial

Kommentar hinterlassen