Ich habe eine Datendatei mit mehreren Datenblöcken, die zwischen bestimmten Schlüsselwörtern eingeschlossen sind (DATA
, END
). Ich verwende awk
, um die Datenblöcke basierend auf einem Dateinamen aus diesem Block in separate Dateien zu extrahieren. Da einige Datenblöcke denselben Namen haben, benenne ich jede Ausgabedatei mit einer ansteigenden Ganzzahl um, wenn die Datei („blockname
„) bereits vorhanden ist:
#cat input.file useless stuff1 DATA blockname1 data1 data1 END useless stuff2 DATA blockname2 data2 data2 END useless stuff3 DATA blockname1 data3 data3 END useless stuff4
Es werden voraussichtlich drei Ausgabedateien blockname1
, blockname2
und (beachten Sie, wie der letzten Datei eine Ganzzahl zugewiesen wurde)
#cat blockname1 DATA blockname1 data1 data1 END
(die anderen entsprechend …)
Jetzt funktioniert das folgende Skript so, wie ich es möchte:
awk "BEGIN { FS=" +" } ; \ /DATA/,/END/ \ { if ( $1 ~ /DATA/ ) \ { block=$2 ; i=0 ; file=block ;\ while ( system("test ! -e " file ) ) \ { i++ ; file=block"_"i ; print file } \ } ; \ print $0 > file \ } " \ input.file
Mein Problem liegt in der while
-Schleife und sein Systemaufruf:
Ich habe erwartet, dass system("test -e " file)
WAHR ist, wenn die file
existiert, und FALSCH ist, wenn file
existiert noch nicht, dh die while
-Schleife startet nur, wenn file
vorhanden ist und zu brechen, wenn (die neue) file
noch nicht existiert.
Wenn ich jedoch (und machen Sie es ausführlich mit print file
), ich habe eine Endlosschleife mit demselben Namen mit zunehmendem Integer-Suffix und dem entgegengesetzten system("test !-e " file)
liefert das gewünschte Ergebnis.
Dies verhält sich also genau umgekehrt zu dem, was ich erwartet habe.
Antwort
OK, ich dachte mir: Das Problem liegt in den unterschiedlichen Definitionen von TRUE und FALSE zwischen dem Exit-Status von test
und while
Schleifenbedingung in awk
.
Ein postiver test
Befehl führt zu einem Exit-Code von 0
für TRUE und eine negative in 1
für FALSE.
In awk
Die Schleife while
interpretiert 0
als FALSE und 1
als TRUE, also genau das Gegenteil Definition.
Als Beispiel:
awk "{ while ( 0 ) ; { print "0" } }" file
wird nicht pro Führen Sie eine Ausgabe aus, während
awk "{ while (1) ; { print "1" } }" file
unendlich 1
s ausgibt.
Best Practice ist daher in einer solchen Kombination explizit
while ( system("command") == 0 )
bzw.
while ( system("command") == 1 )
.
In meinem Fall
while ( system("test -e " file ) == 0 )
zeigt das erwartete Verhalten.
Antwort
awk
system()
gibt einen Beendigungsstatus des von Ihnen ausgeführten Befehls zurück – 0 für Erfolg und! = 0 wenn kein Erfolg. Als einfaches Beispiel können Sie versuchen, Folgendes auszuführen:
v = system("date");
v ist 0
, wenn Sie Folgendes ausführen:
v = system("dat");
v ist möglicherweise 127 oder ein anderer Wert als 0, der vom Betriebssystem zurückgegebene Fehler, wenn der Befehl dat fehlt oder nicht gefunden wird.
Antwort
Wenn ich Sie verstehe, besteht das Ziel darin, den Inhalt der Eingabedatei in verschiedene Dateien zu extrahieren, um zu vermeiden, dass gleichnamige Blöcke verloren gehen.
Wenn dies der Fall ist Wenn das Zielverzeichnis vor der Extraktion immer leer ist, gibt es eine bessere (und schnellere) Lösung:
awk " /DATA/{ block=$2; n = blocks[block]++; file=block (n? "_" n: ""); } /DATA/,/END/{ print > file }" input.file
Auf diese Weise funktioniert awk nicht „Sie müssen keine neue Shell N-mal ausführen, um zu testen, ob eine Datei vorhanden ist.
Hinweise:
- Der BEGIN-Block ist nicht erforderlich, da das Feldtrennzeichen von awk vorhanden ist Es gibt bereits Leerzeichen.
- Das
"\"
am Ende der Zeilen ist nicht erforderlich, da das einfache Anführungszeichen bereits mehrzeilig ist.
Kommentare
- Nun, mein Problem war mehr im Verständnis der “ ungeraden “ Verhalten der
while
Bedingung. Ihre Lösung ist jedoch ziemlich ordentlich – danke, ich bevorzuge dies gegenüber meinem sperrigen Ding. Der BEGIN-Block ist ein Überbleibsel aus meinem anderen Dateiformat – ich habe vergessen, dass er in meinem allgemeinen Beispiel unbrauchbar wird. Das Weglassen des Backslash erspart mir einige Probleme. Danke auch dafür. Aber Sie sind genau richtig in Bezug auf die Absicht des Skripts ‚. Können Sie den Teiln = blocks[block]++; file=block (n? "_" n: "")
genauer erläutern? - Der Algorithmus verwendet ein Array:
blocks
, das indiziert ist nach Blockname. Ex. In der ersten Instanz von"blockname1"
:blocks["blockname1"]
. Awk finde diesen Index und da er nicht ‚ nicht gefunden wird, wird""
angenommen (auch als Null betrachtet). In awk entsprichtn = var++
{n=var;var++}
, alson==""
undblocks["blockname1"]==1
.Schließlich istfile=block (n? "_" n: "")
dasselbe wie{file=block;if(n!="") file+="_" n}
. - Ich war nicht ‚ Ich weiß nicht einmal, dass ein String eine gültige Indizierungsvariable ist, und diese reduzierte if-Anweisung ist sehr hilfreich, nochmals vielen Dank. Ich ‚ bin traurig, dass ich Ihnen hier nicht die “ akzeptierte Antwort “ geben kann: trotz mir Wenn ich diesen Ansatz anwende, da er meine anfängliche Aufgabe erfüllt, ‚ die obige Frage nicht beantwortet (Interpretation der Bedingung in while-Schleife) – dies würde meinem Verständnis dieser Sites Q widersprechen & Ein System. Trotzdem habe ich viel gelernt – nochmals vielen Dank.