awk systemanrop med inverterad effekt

Jag har en datafil med flera datablock som är inneslutna mellan specifika nyckelord (DATA , END). Jag använder awk för att extrahera datablocken i separata filer, baserat på ett filnamn som tagits från blocket. Eftersom vissa datablock har samma namn, byter jag namn på varje utdatafil med ett ökande heltal om filen (”blockname”) redan finns:

#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 

Förväntas vara tre utdatafiler blockname1, blockname2 och blockname1_1 (notera hur den sista filen har tilldelats ett heltal)

#cat blockname1 DATA blockname1 data1 data1 END 

(de andra följaktligen …)

Nu fungerar följande skript som jag vill ha det:

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 

Mitt problem ligger i while -slingan och dess systemanrop:

Jag förväntade mig att system("test -e " file) skulle vara SANT när file existerar och att vara FALSK om file finns inte ännu, dvs while slingan för att bara börja köra om file är närvarande och att bryta om (den nya) file inte finns ännu.

Men om jag använder system("test -e " file) (och gör det ordentligt med print file), jag har en oändlig loop med samma namn med ökande heltalssuffix och motsatsen system("test !-e " file) ger önskat resultat.

Så detta beter sig exakt inverterat till vad jag förväntade mig.

Svar

OK, jag tänkte: problemet ligger i de olika definitionerna av vad som är SANT och FALSKT mellan utgångsstatus för test och while loop-tillstånd i awk.

Ett postivt test -kommando resulterar i en utgångskod 0 för SANT och en negativ i 1 för FALSE.

Men i awk while slingan tolkar 0 som FALSE och 1 som SANT, så exakt motsatt definition.

Som ett exempel:

awk "{ while ( 0 ) ; { print "0" } }" file 

kommer inte att duce någon output, medan

awk "{ while (1) ; { print "1" } }" file 

kommer att skriva ut oändliga 1 s.

Bästa praxis ska således uttryckas i en sådan kombination

while ( system("command") == 0 ) 

eller

while ( system("command") == 1 ) 

.

Så i mitt fall

while ( system("test -e " file ) == 0 ) 

visar det förväntade beteendet.

Svar

awk system() returnerar en utgångsstatus för kommandot du kör – 0 för framgång och! = 0 om inte framgång. För ett enkelt exempel kan du försöka köra:

v = system("date"); 

v blir 0

om du kör:

v = system("dat"); 

v kan vara 127 eller värdet skiljer sig från 0, felet returneras från OS om dat-kommandot saknas eller inte hittas.

Svar

Om jag förstår dig är målet att extrahera innehållet i input.file i olika filer för att undvika att förlora block med samma namn.

Om det är om målkatalogen alltid är tom före extraktionen, så finns det en bättre (och snabbare) lösning:

awk " /DATA/{ block=$2; n = blocks[block]++; file=block (n? "_" n: ""); } /DATA/,/END/{ print > file }" input.file 

På detta sätt gör awk inte ”t behöver köra ett nytt skal N gånger bara för att testa om filen finns.

Anmärkningar:

  • BEGIN-blocket behöver inte, eftersom awks fältseparator är redan mellanslag.
  • Det finns inget behov av "\" i slutet av raderna, eftersom det enda citatet redan är flerlinjigt.

Kommentarer

  • Tja, mitt problem var mer förståelse för " udda " beteende för while tillstånd. Men din lösning är ganska snygg – tack, jag föredrar det här framför min skrymmande sak. BEGIN-blocket är en rest från mitt olika filformat – jag glömde att det blir värdelöst i mitt generiska exempel. Att lämna backslash kommer att spara mig lite problem. Tack också för detta. Men du känner igen skriptets ' avsikt. Kan du förklara n = blocks[block]++; file=block (n? "_" n: "") -delen mer detaljerat?
  • Algoritmen använder en matris: blocks som är indexerad efter blocknamn. Ex. I första hand av "blockname1": blocks["blockname1"]. Awk hitta det indexet och eftersom det inte ' t hittas antar "" (anses också vara noll). I awk motsvarar n = var++ {n=var;var++}, så n=="" och blocks["blockname1"]==1.Slutligen är file=block (n? "_" n: "") samma som {file=block;if(n!="") file+="_" n}.
  • Jag var inte ' inte ens medveten om att en sträng är en giltig indexeringsvariabel och är detta reducerade if-uttalande – mycket användbart, tack igen. Jag ' är bedrövad att jag inte kan ge dig " accepterat svar " här: trots mig kommer att anta detta tillvägagångssätt när det fullgör min ursprungliga uppgift, det svarar inte ' på frågan ovan (tolkning av villkor i en loop) – det skulle strida mot min förståelse av dessa platser Q & Ett system. Ändå lärde jag mig mycket – tack igen.

Lämna ett svar

Din e-postadress kommer inte publiceras. Obligatoriska fält är märkta *