2010-03-06 10 views
5

Ich versuche, Daten von einem Format in ein anderes zu konvertieren: Von z.B. "29. Oktober 2005" bis 2005-10-29. Ich habe eine Liste von 625 Daten. Ich benutze Awk.gawk/awk: Piping-Datum zu getline * manchmal * wird nicht funktionieren

Die Umwandlung funktioniert - die meiste Zeit. Hovewer, manchmal wird die Konvertierung überhaupt nicht passieren, und die Variable, die das (konvertierte) Datum enthalten soll, bleibt undefined.

Dies geschieht immer mit den exakt gleichen Zeilen. Running `date' explizit (aus der Bash-Shell) an den Daten dieser seltsamen Zeilen funktioniert gut (die Daten werden ordnungsgemäß konvertiert). - Es kommt nicht auf den Textinhalt dieser Zeilen an.

Warum dieses Verhalten, und wie kann ich mein Skript reparieren?
Her ist:

awk 'BEGIN { FS = "unused" } { 
    x = "undefined"; 
    "date \"+%Y-%m-%d\" -d " $1 | getline x ; 
    print $1 " = " x 
}' uBXr0r15.txt \ 
> bug-out-3.txt 

Wenn Sie das Problem reproduzieren wollen:

  1. Diese Datei herunterladen: uBXr0r15.txt.
  2. Führen Sie das Awk-Skript aus.
  3. Suche nach "undefined" in Bug-out-3.txt.
    ("undefiniert" gefunden 122 mal, auf meinem Computer.)

Dann wieder das Skript Sie laufen konnte, und (auf meinem Computer) bug-out-3.txt unverändert bleibt - genau das Die gleichen Daten sind nicht definiert.

(Gawk Version 3.1.6, Ubuntu 9.10.)

Mit freundlichen Grüßen Magnus

Antwort

8

Jedes Mal, wenn Sie ein Rohr oder eine Datei zum Lesen öffnen oder in awk schreibt, wird diese zunächst prüfen (eine interne Hash verwenden) , ob es bereits ein Rohr hat oder mit dem gleichen Namen Datei (immer noch offen; Wenn ja, wird der vorhandene Dateideskriptor wiederverwendet, anstatt die Pipe oder Datei erneut zu öffnen.

In Ihrem Fall sind alle Einträge, die als undefined enden, tatsächlich Duplikate; beim ersten Auftreten (d. h. wenn der entsprechende Befehl date "..." -d "..." zum ersten Mal ausgegeben wird) wird das richtige Ergebnis in x eingelesen. Bei nachfolgenden Vorkommen desselben Datums versucht getline, eine zweite, dritte usw. Zeile aus der ursprünglichen date-Pipe zu lesen, obwohl die Pipe durch date geschlossen wurde, was dazu führt, dass x nicht mehr zugewiesen wird.

Von gawk Mann-Seite:

HINWEIS: Wenn ein Rohr mit Co-Prozess oder Buchse getline oder von Druck oder printf innerhalb einer Schleife, Sie müssen in der Nähe nutzen() um neue Instanzen des Befehls oder Sockets zu erstellen. AWK schließt nicht automatisch Pipes, Sockets oder Co-Prozesse , wenn sie EOF zurückgeben.

Sie sollten explizit close das Rohr jedes Mal, nachdem Sie x lesen:

close("date \"+%Y-%m-%d\" -d " $1) 

Im Übrigen wäre es zu sort und uniquBXr0r15.txt vor kochend in awk in Ordnung sein, oder benötigen Sie die ursprüngliche Reihenfolge /Vervielfältigung?

+0

Das löst mein Problem, danke. Ich brauche nicht die ursprüngliche Reihenfolge und wenn ich meine Indata neu anordne, ist das Problem auch weg - und ich spare etwas CPU, nehme ich an. (Die reale Welt indata enthält auch Nicht-Datumszeilen, so kann ich nicht 'sort 'und' uniq' verwenden, und das echte Awk-Skript ist etwas anders.) – KajMagnus

+0

Danke, das löst auch mein Problem - ich bekam einen Fehler "Zu viele offene Dateien" und fragte mich, wie man diese "Dateien" schließen könnte, weil ich nicht wusste, dass awk Dateien für diese Pipe-Operationen verwendet. – flo

+0

Ich bemerkte dieses Problem beim nachfolgenden Aufruf einer Funktion, um zufällig zu resedieren. –

3

Obwohl ich liebe awk es nicht notwendig, diese ist.

tr -d '"' < uBXr0r15.txt | date +%Y-%m-%d -f -

+0

Danke, wusste nicht, dass ich das tun könnte. Das Beispiel in meinem ursprünglichen Beitrag ist ein vereinfachtes Beispiel. Mein echtes Awk-Skript ist etwas länger, und die echte Eingabedatei enthält auch Nicht-Datumszeilen. – KajMagnus

3
gawk 'BEGIN{ 
     m=split("January|February|March|April|May|June|July|August|September|October|November|December",d,"|") 
     for(o=1;o<=m;o++){ 
      months[d[o]]=sprintf("%02d",o) 
     } 
     FS="[, ]" 
    } 
    { 
     gsub(/["]/,"",$1) 
     gsub(/["]/,"",$4) 
     t=mktime($4" "months[$1]" "$2" 0 0 0") 
     print strftime("%Y-%m-%d",t) 
    }' uBXr0r15.txt 

tun alles in gawk wird schneller sein als externe Befehle aufrufen.

+0

Das war eine ehrgeizige Lösung :-) Funktioniert gut. Es ist in Ordnung für mich, diese Sekundenbruchteile länger zu warten, bis die anderen Lösungen greifen :-) – KajMagnus