Jeg har en datafil med flere datablokke lukket mellem specifikke nøgleord (DATA
, END
). Jeg bruger awk
til at udtrække datablokkene i separate filer, baseret på et filnavn taget fra blokken. Da nogle datablokke har samme navn, omdøber jeg hver outputfil med et stigende heltal, hvis filen (“blockname
“) allerede findes:
#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
Forventes at være tre outputfiler blockname1
, blockname2
og blockname1_1
(bemærk, hvordan den sidste fil har et heltal tildelt den)
#cat blockname1 DATA blockname1 data1 data1 END
(de andre i overensstemmelse hermed …)
Nu fungerer følgende script som jeg vil have 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
Mit problem ligger i while
loop og dets systemopkald:
Jeg forventede, at system("test -e " file)
ville være SAND, når file
eksisterer og være FALSK, hvis file
eksisterer endnu ikke, dvs. while
loop kun for at starte, hvis file
er til stede og at bryde, hvis (den nye) file
ikke findes endnu.
Men hvis jeg bruger system("test -e " file)
(og gør det ordentligt med print file
), jeg har en uendelig løkke med samme navn med stigende heltalssuffiks og det modsatte system("test !-e " file)
giver det ønskede resultat.
Så dette opfører sig nøjagtigt omvendt til, hvad jeg forventede.
Svar
OK, jeg regnede med: problemet ligger i de forskellige definitioner af, hvad der er SAND og FALSK mellem udgangsstatus for test
og while
loop-tilstand i awk
.
En postiv test
-kommando resulterer i en udgangskode på 0
for SAND og en negativ i 1
for FALSE.
Imidlertid i awk
while
loop fortolker 0
som FALSE og 1
som SAND, så nøjagtigt det modsatte definition.
Som et eksempel:
awk "{ while ( 0 ) ; { print "0" } }" file
vil ikke være duce enhver output, mens
awk "{ while (1) ; { print "1" } }" file
vil udskrive uendelig 1
s.
Bedste praksis skal således være eksplicit i en sådan kombination
while ( system("command") == 0 )
eller
while ( system("command") == 1 )
.
Så i mit tilfælde
while ( system("test -e " file ) == 0 )
viser den forventede adfærd.
Svar
awk
system()
returnerer en udgangsstatus for kommandoen, du kører – 0 for succes og! = 0 hvis ikke succes. For et simpelt eksempel kan du prøve at køre:
v = system("date");
v vil være 0
hvis du løber:
v = system("dat");
v kan være 127 eller en anden værdi end 0, fejlen returneres fra OS, hvis dat-kommandoen mangler eller ikke findes.
Svar
Hvis jeg forstår dig, er målet at udtrække indholdet af input.file i forskellige filer og undgå at miste blokke med samme navn.
Hvis det er tilfældet, og hvis målkataloget altid er tom før udtrækningen, er der en bedre (og hurtigere) løsning:
awk " /DATA/{ block=$2; n = blocks[block]++; file=block (n? "_" n: ""); } /DATA/,/END/{ print > file }" input.file
På denne måde gør awk ikke “behøver at udføre en ny skal N gange bare for at teste, om der findes en fil.
Bemærkninger:
- BEGIN-blokken er ikke nødvendigt, fordi awks feltseparator er allerede mellemrum.
- Der er ikke behov for
"\"
i slutningen af linjerne, fordi det enkelte citat allerede er multiline.
while
-tilstanden. Men din løsning er temmelig pæn – tak, jeg foretrækker det over min klodsede ting. BEGIN-blokken er en rest fra mit forskellige filformat – jeg glemte, at det bliver ubrugeligt i mit generiske eksempel. At undlade tilbageslag vil spare mig for nogle problemer. Tak også for dette. Men du er lige ved med hensyn til scriptets ' intention. Kunne du forklaren = blocks[block]++; file=block (n? "_" n: "")
-delen mere detaljeret?blocks
, der er indekseret efter bloknavn. Eks. I første omgang af"blockname1"
:blocks["blockname1"]
. Awk finder det indeks, og fordi det ikke er ' t fundet antager""
(også betragtet som nul). I awk svarern = var++
til{n=var;var++}
, sån==""
ogblocks["blockname1"]==1
.Endelig erfile=block (n? "_" n: "")
det samme som{file=block;if(n!="") file+="_" n}
.