awk systeemaanroep met omgekeerd effect

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.

Reacties

  • Nou, mijn probleem was meer in het begrijpen van het ” oneven ” gedrag van de 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 het n = blocks[block]++; file=block (n? "_" n: "") -gedeelte wat gedetailleerder uitleggen?
  • Het algoritme gebruikt één array: 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 awk n = var++ gelijk aan {n=var;var++}, dus n=="" en blocks["blockname1"]==1.Eindelijk is file=block (n? "_" n: "") hetzelfde als {file=block;if(n!="") file+="_" n}.
  • Ik was niet ‘ Ik ben me er niet eens van bewust dat een string een geldige indexvariabele is en dat is deze gereduceerde if-statement – erg nuttig, nogmaals bedankt. Ik ‘ ben bedroefd dat ik je het ” geaccepteerde antwoord ” hier niet kan geven: ondanks mij als ik deze benadering ga toepassen omdat het mijn initiële taak vervult, ‘ beantwoordt het de bovenstaande vraag niet (interpretatie van voorwaarde in while-lus) – het zou in strijd zijn met mijn begrip van deze site Q & Een systeem. Toch heb ik veel geleerd – nogmaals bedankt.

Geef een reactie

Het e-mailadres wordt niet gepubliceerd. Vereiste velden zijn gemarkeerd met *