awk systemanrop med omvendt effekt

Jeg har en datafil med flere datablokker som er lukket mellom bestemte nøkkelord (DATA , END). Jeg bruker awk for å trekke ut datablokkene i separate filer, basert på et filnavn hentet fra blokken. Siden noen datablokker har samme navn, omdøper jeg hver utdatafil med et økende heltall hvis filen («blockname«) allerede eksisterer:

#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 

Forventet ville være tre utdatafiler blockname1, blockname2 og blockname1_1 (merk hvordan den siste filen har et heltall tildelt den)

#cat blockname1 DATA blockname1 data1 data1 END 

(de andre tilsvarende …)

Nå fungerer følgende skript som jeg vil 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 

Problemet mitt ligger i while loop og systemanropet:

Jeg forventet at system("test -e " file) skulle være SANT når file eksisterer og å være FALSE hvis file eksisterer ennå ikke, dvs. while sløyfen som bare begynner å kjøre hvis file er tilstede og å bryte hvis (den nye) file ikke eksisterer ennå.

Men hvis jeg bruker system("test -e " file) (og gjør det ordentlig med print file), jeg har en uendelig løkke med samme navn med økende heltalssuffiks og det motsatte system("test !-e " file) gir ønsket resultat.

Så dette oppfører seg nøyaktig omvendt til det jeg forventet.

Svar

OK, jeg skjønte: problemet ligger i de forskjellige definisjonene av hva som er SANT og FALSK mellom utgangsstatusen til test og while sløyfetilstand i awk.

En postiv test -kommando resulterer i en utgangskode på 0 for SANT og negativ i 1 for FALSE.

Imidlertid, i awk while sløyfen tolker 0 som FALSE og 1 som SANT, så akkurat det motsatte definisjon.

Som et eksempel:

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

vil ikke pro duce hvilken som helst utgang, mens

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

vil skrive ut uendelig 1 s.

Beste praksis er således å være eksplisitt i en slik kombinasjon

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

eller

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

.

Så i mitt tilfelle

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

viser forventet oppførsel.

Svar

awk system() returnerer en utgangsstatus for kommandoen du kjører – 0 for suksess og! = 0 hvis ikke suksess. For et enkelt eksempel kan du prøve å kjøre:

v = system("date"); 

v blir 0

hvis du løper:

v = system("dat"); 

v kan være 127 eller verdien er forskjellig fra 0, feilen returneres fra operativsystemet hvis dat-kommandoen mangler eller ikke blir funnet.

Svar

Hvis jeg forstår deg, er målet å trekke ut innholdet i input.file i forskjellige filer, og unngå å miste blokker med samme navn.

Hvis det er tilfelle, og hvis målkatalogen alltid er tom før ekstraksjonen, er det en bedre (og raskere) løsning:

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

På denne måten gjør awk ikke «t trenger å utføre et nytt skall N ganger bare for å teste om filen eksisterer.

Merknader:

  • BEGIN-blokken er ikke nødvendig, fordi awks feltskiller er allerede mellomrom.
  • Det er ikke behov for "\" på slutten av linjene, fordi enkelt sitatet allerede er flerlinjet.

Kommentarer

  • Vel, problemet mitt var mer i å forstå " odd " oppførselen til while -tilstanden. Løsningen din er imidlertid ganske ryddig – takk, jeg foretrekker, dette fremfor min klumpete ting. BEGIN-blokken er en rest fra mitt forskjellige filformat – jeg glemte at den blir ubrukelig i mitt generelle eksempel. Å utelate tilbakeslaget vil spare meg for noen problemer. Takk for dette også. Men du er klar over angående skriptets ' intensjon. Kan du forklare n = blocks[block]++; file=block (n? "_" n: "") -delen mer detaljert?
  • Algoritmen bruker en matrise: blocks som er indeksert etter blokknavn. Eks. I første omgang av "blockname1": blocks["blockname1"]. Awk finner den indeksen, og fordi den ikke er ' t funnet antar "" (også ansett som null). Nå tilsvarer n = var++ {n=var;var++}, så n=="" og blocks["blockname1"]==1.Endelig er file=block (n? "_" n: "") det samme som {file=block;if(n!="") file+="_" n}.
  • Jeg var ikke ' ikke engang klar over at en streng er en gyldig indekseringsvariabel, og av er denne reduserte if-setningen – veldig nyttig, takk igjen. Jeg ' er lei meg, jeg kan ikke gi deg " akseptert svar " her: til tross for meg kommer til å bruke denne tilnærmingen når den oppfyller den første oppgaven min, svarer den ikke ' t på spørsmålet ovenfor (tolkning av tilstand i mens sløyfe) – det ville stride mot min forståelse av disse stedene Q & Et system. Likevel lærte jeg mye – takk igjen.

Legg igjen en kommentar

Din e-postadresse vil ikke bli publisert. Obligatoriske felt er merket med *