Ik heb een gegevensbestand met meerdere gegevensblokken ingesloten tussen specifieke trefwoorden (DATA
, END
). Ik gebruik awk
om de datablokken in afzonderlijke bestanden te extraheren, op basis van een bestandsnaam die uit dat blok is gehaald. Aangezien sommige datablokken dezelfde naam hebben, hernoem ik elk uitvoerbestand met een oplopend geheel getal als het bestand (“blockname
“) al bestaat:
#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
Verwacht worden drie uitvoerbestanden blockname1
, blockname2
en blockname1_1
(merk op hoe aan het laatste bestand een geheel getal is toegewezen)
#cat blockname1 DATA blockname1 data1 data1 END
(de andere dienovereenkomstig …)
Nu werkt het volgende script zoals ik het wil:
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
Mijn probleem ligt bij de while
lus en zijn systeemaanroep:
Ik verwachtte dat system("test -e " file)
WAAR zou zijn wanneer de file
bestaat en ONWAAR zou zijn als file
bestaat nog niet, dwz de while
-lus om alleen te starten als file
aanwezig is en te breken als (de nieuwe) file
nog niet bestaat.
Als ik echter (en maak het uitgebreid met print file
), ik heb een oneindige lus met dezelfde naam met een toenemend integer achtervoegsel en het tegenovergestelde system("test !-e " file)
geeft het gewenste resultaat.
Dus dit gedraagt zich precies omgekeerd aan wat ik had verwacht.
Antwoord
OK, ik dacht: het probleem ligt bij de verschillende definities van wat WAAR en ONWAAR is tussen de exitstatus van test
en de while
lusvoorwaarde in awk
.
Een positief test
commando resulteert in een afsluitcode van 0
voor TRUE en een negatieve in 1
voor FALSE.
Echter, in awk
de while
-lus interpreteert 0
als FALSE en 1
als TRUE, dus precies het tegenovergestelde definitie.
Als voorbeeld:
awk "{ while ( 0 ) ; { print "0" } }" file
zal niet pro dubbele uitvoer, terwijl
awk "{ while (1) ; { print "1" } }" file
oneindig 1
s zal afdrukken.
Beste praktijk moet dus expliciet zijn in een dergelijke combinatie
while ( system("command") == 0 )
of
while ( system("command") == 1 )
respectievelijk.
Dus in mijn geval
while ( system("test -e " file ) == 0 )
toont het verwachte gedrag.
Antwoord
awk
system()
retourneert een afsluitstatus van de opdracht die u uitvoert – 0 voor succes en! = 0 zo niet succes. Voor een eenvoudig voorbeeld kunt u proberen:
v = system("date");
v zal 0 zijn
als u uitvoert:
v = system("dat");
v kan 127 zijn of een andere waarde dan 0, de fout die wordt geretourneerd door het besturingssysteem als dat commando ontbreekt of niet wordt gevonden.
Antwoord
Als ik je begrijp, is het doel de inhoud van input.file uit te pakken in verschillende bestanden en blokken met dezelfde naam te vermijden.
Als dat het geval is geval, en als de doeldirectory altijd leeg is vóór de extractie, dan is er een betere (en snellere) oplossing:
awk " /DATA/{ block=$2; n = blocks[block]++; file=block (n? "_" n: ""); } /DATA/,/END/{ print > file }" input.file
Op deze manier doet awk “Het is niet nodig N keer een nieuwe shell uit te voeren om te testen of het bestand bestaat.
Opmerkingen:
- Het BEGIN-blok is niet nodig, omdat awks veldscheidingsteken is al spaties.
- De
"\"
aan het einde van de regels is niet nodig, omdat het enkele aanhalingsteken al uit meerdere regels bestaat.
while
voorwaarde. Uw oplossing is echter behoorlijk netjes – dank u, ik geef de voorkeur aan dit boven mijn omvangrijke ding. Het BEGIN-blok is een overblijfsel van mijn andere bestandsindeling – ik vergat dat het nutteloos werd in mijn generieke voorbeeld. Het weglaten van de backslash zal me wat moeite besparen. Ook hiervoor bedankt. Maar je bent precies wat betreft de bedoeling van het script ‘. Kun je hetn = blocks[block]++; file=block (n? "_" n: "")
-gedeelte wat gedetailleerder uitleggen?blocks
die is geïndexeerd op bloknaam. Ex. In het eerste geval van"blockname1"
:blocks["blockname1"]
. Awk vind die index en omdat het niet ‘ is, wordt ervan uitgegaan dat""
(ook beschouwd als nul). Nu is in awkn = var++
gelijk aan{n=var;var++}
, dusn==""
enblocks["blockname1"]==1
.Eindelijk isfile=block (n? "_" n: "")
hetzelfde als{file=block;if(n!="") file+="_" n}
.