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.
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örklaran = blocks[block]++; file=block (n? "_" n: "")
-delen mer detaljerat?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 motsvararn = var++
{n=var;var++}
, sån==""
ochblocks["blockname1"]==1
.Slutligen ärfile=block (n? "_" n: "")
samma som{file=block;if(n!="") file+="_" n}
.