Mám datový soubor s více datovými bloky uzavřenými mezi konkrétními klíčovými slovy (DATA
, END
). Pomocí awk
extrahuji datové bloky do samostatných souborů na základě názvu souboru převzatého z uvedeného bloku. Jelikož některé datové bloky sdílejí stejný název, přejmenovávám každý výstupní soubor s rostoucím celým číslem, pokud soubor („blockname
„) již existuje:
#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
Očekávají se tři výstupní soubory blockname1
, blockname2
a blockname1_1
(všimněte si, jak má poslední soubor přiřazeno celé číslo)
#cat blockname1 DATA blockname1 data1 data1 END
(ostatní odpovídajícím způsobem …)
Nyní následující skript funguje tak, jak chci:
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
Můj problém spočívá ve smyčce while
a jeho systémové volání:
Očekával jsem, že system("test -e " file)
bude TRUE, když file
bude existovat a že bude NEPRAVDA, pokud file
dosud neexistuje, tj. smyčka while
se spustí, pouze pokud je file
přítomna a rozbít, pokud (nový) file
dosud neexistuje.
Pokud však použiji system("test -e " file)
(a učiním to podrobným s print file
), mám nekonečnou smyčku se stejným názvem se zvětšující se celočíselnou příponou a opačným system("test !-e " file)
dává požadovaný výsledek.
Takže toto se chová přesně opačně, než jsem očekával.
Odpovědět
OK, napadlo mě: problém spočívá v různých definicích toho, co je PRAVDA a NEPRAVDA mezi stavem ukončení test
a while
podmínka smyčky v awk
.
Výsledkem postivního test
kódu ukončení je 0
pro TRUE a záporný pro 1
pro FALSE.
Avšak v awk
smyčka while
interpretuje 0
jako FALSE a 1
jako TRUE, takže přesně naopak definice.
Jako příklad:
awk "{ while ( 0 ) ; { print "0" } }" file
nebude pro zkuste jakýkoli výstup, zatímco
awk "{ while (1) ; { print "1" } }" file
vytiskne nekonečné 1
s.
Osvědčené postupy je tedy explicitní v takové kombinaci
while ( system("command") == 0 )
nebo
while ( system("command") == 1 )
.
Takže v mém případě
while ( system("test -e " file ) == 0 )
ukazuje očekávané chování.
Odpověď
awk
system()
vrátí stav ukončení spuštěného příkazu – 0 pro úspěch a! = 0, pokud ne úspěch. Pro jednoduchý příklad můžete zkusit spustit:
v = system("date");
v bude 0, pokud spustíte:
v = system("dat");
v může být 127 nebo hodnota odlišná od 0, chyba vrácená z OS, pokud příkaz dat chybí nebo nebyl nalezen.
Odpověď
Pokud vám rozumím, cílem je extrahovat obsah souboru input.file do různých souborů, aby nedocházelo ke ztrátě bloků se stejným názvem.
Pokud je to case, a pokud je cílový adresář před extrakcí vždy prázdný, existuje lepší (a rychlejší) řešení:
awk " /DATA/{ block=$2; n = blocks[block]++; file=block (n? "_" n: ""); } /DATA/,/END/{ print > file }" input.file
Tímto způsobem awk doesn Není potřeba spustit nový shell N krát, jen aby se otestovalo, zda soubor existuje.
Poznámky:
- Blok ZAČÍT není potřeba, protože oddělovač polí awk je již mezery.
- Na konci řádků není třeba
"\"
, protože jednoduchá nabídka je již víceřádková.
while
. Vaše řešení je však docela úhledné – děkuji, dávám přednost, toto přes mou objemnou věc. BEGIN blok je pozůstatek z mého jiného formátu souboru – zapomněl jsem, že se v mém obecném příkladu stane zbytečným. Vynechání zpětného lomítka mi ušetří nějaké potíže. Díky za to taky. Ale jste na místě ohledně záměru skriptu '. Můžete vysvětlit částn = blocks[block]++; file=block (n? "_" n: "")
podrobněji?blocks
, které je indexováno podle názvu bloku. Př. V první instanci"blockname1"
:blocks["blockname1"]
. Zdá se, že tento index našel, a protože není nalezen, předpokládá""
(také považován za nulu). Nyní je v awkn = var++
ekvivalent{n=var;var++}
, takžen==""
ablocks["blockname1"]==1
.Nakonec jefile=block (n? "_" n: "")
stejný jako{file=block;if(n!="") file+="_" n}
.