Ho un file di dati con più blocchi di dati racchiusi tra parole chiave specifiche (DATA
, END
). Sto usando awk
per estrarre i blocchi di dati in file separati, in base a un nome di file preso da detto blocco. Poiché alcuni blocchi di dati condividono lo stesso nome, rinomino ogni file di output con un numero intero crescente se il file (“blockname
“) esiste già:
#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
Sono previsti tre file di output blockname1
, blockname2
e blockname1_1
(nota come lultimo file ha un numero intero assegnato)
#cat blockname1 DATA blockname1 data1 data1 END
(gli altri di conseguenza …)
Ora il seguente script funziona come voglio:
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
Il mio problema risiede con il ciclo while
e la sua chiamata di sistema:
Mi aspettavo che system("test -e " file)
fosse TRUE quando file
esiste e che fosse FALSE se file
non esiste ancora, ovvero il while
ciclo da avviare solo se file
è presente e rompere se (il nuovo) file
non esiste ancora.
Tuttavia, se uso system("test -e " file)
(e rendilo dettagliato con print file
), ho un ciclo infinito con lo stesso nome con suffisso intero crescente e system("test !-e " file)
fornisce il risultato desiderato.
Quindi si comporta esattamente in modo inverso a quello che mi aspettavo.
Risposta
OK, ho pensato: il problema risiede nelle diverse definizioni di cosa è VERO e FALSO tra lo stato di uscita di test
e while
condizione del ciclo in awk
.
Un comando test
postive restituisce un codice di uscita 0
per TRUE e uno negativo in 1
per FALSE.
Tuttavia, in awk
il while
loop interpreta 0
come FALSE e 1
come TRUE, quindi esattamente lopposto definizione.
Ad esempio:
awk "{ while ( 0 ) ; { print "0" } }" file
non pro duce qualsiasi output, mentre
awk "{ while (1) ; { print "1" } }" file
stamperà infiniti 1
s.
Best practice deve quindi essere esplicito in tale combinazione
while ( system("command") == 0 )
o
while ( system("command") == 1 )
rispettivamente.
Quindi nel mio caso
while ( system("test -e " file ) == 0 )
mostra il comportamento previsto.
Risposta
awk
system()
restituisce uno stato di uscita del comando che esegui: 0 per successo e! = 0 se non ha successo. Per un semplice esempio puoi provare a eseguire:
v = system("date");
v sarà 0
se esegui:
v = system("dat");
v potrebbe essere 127 o un valore diverso da 0, lerrore restituito dal sistema operativo se il comando dat è mancante o non trovato.
Risposta
Se ti capisco, lobiettivo è estrarre il contenuto di input.file in vari file evitando di perdere blocchi con lo stesso nome.
Se questo è il caso e, se la directory di destinazione è sempre vuota prima dellestrazione, allora cè una soluzione migliore (e più veloce):
awk " /DATA/{ block=$2; n = blocks[block]++; file=block (n? "_" n: ""); } /DATA/,/END/{ print > file }" input.file
In questo modo awk non “Non è necessario eseguire una nuova shell N volte solo per verificare se il file esiste.
Note:
- Non è necessario il blocco BEGIN, perché il separatore di campo di awk sono già spazi.
- Non è necessario
"\"
alla fine delle righe, perché le virgolette singole sono già su più righe.
while
. Comunque la tua soluzione è abbastanza chiara – grazie, preferisco questa rispetto alla mia cosa ingombrante. Il blocco BEGIN è un residuo del mio diverso formato di file: ho dimenticato che diventa inutile nel mio esempio generico. Tralasciare il backslash mi risparmierà qualche problema. Grazie anche per questo. Ma sei a posto per quanto riguarda lintenzione dello script '. Potresti spiegare la parten = blocks[block]++; file=block (n? "_" n: "")
in modo più dettagliato?blocks
che è indicizzato per nome del blocco. Ex. Nella prima istanza di"blockname1"
:blocks["blockname1"]
. Awk, trova quellindice e poiché non è ' t trovato assume""
(considerato anche zero). Ora, in awkn = var++
è equivalente a{n=var;var++}
, quindin==""
eblocks["blockname1"]==1
.Infinefile=block (n? "_" n: "")
è uguale a{file=block;if(n!="") file+="_" n}
.