← Journal · Archiv

awk Tutorial, Part 2

October 24, 2005

Every now and then, as a programmer, you get data from two sources that have to be merged together. That is actually one of the standard tasks in my job. awk is very flexible for this kind of work and thereby saves me a lot of effort.
Often you also get the CSV data in varying quality and format. Because CSV (Comma Separated Values) is not just CSV. In the first part of my mini-tutorial we had a tab-separated CSV (occasionally also called TSV).
Besides tabs, semicolons are common too. I favour two consecutive pipes (||); since they really do occur very rarely in text, they are ideally suited.
As we have already learned how to change the field separator in awk, the different formats are no problem. This time, as an example, I want to make the product database of the Copyleftstore (plug, plug) fit for Froogle. To the products in one text file I want to add the links to their images and their product pages from another text file.
Merging two sources requires somewhat more elaborate code and the use of the BEGIN and END clauses, which I already introduced in the first part.
In the BEGIN clause we load the product catalogue that we have defined in the variable source.

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 = "\|\|";
}

We have now packed the contents of source into an array. In the next clause we load the image information from images.txt line by line and check in a for loop whether we can find the matching product in the array. Of course you could also load the images first and then the products.

{
  #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;
    }
  }
}

Finally we print the prepared array, complete with header, to STDOUT.

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];
  }
}

We start the whole thing with the following command line. It passes the variable source via the -v option switch.

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

Done, the final result can now be uploaded to Froogle.
The icing on the cake would be to optimise the code so that the little for loop only runs through the lines that have not yet been assigned an image. That would be possible by printing directly to STDOUT. Products that have already been matched would be deleted from the array, which would make the for loop faster and faster towards the end. In END only those articles would then be printed for which no image was found. The advantage is obvious: with a product range of just 41 products the performance is fine. With 410,000 it is a different story.

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