Mam plik danych z wieloma blokami danych zawartymi między określonymi słowami kluczowymi (DATA
, END
). Używam awk
do wyodrębnienia bloków danych do oddzielnych plików, na podstawie nazwy pliku pobranej z wymienionego bloku. Ponieważ niektóre bloki danych mają tę samą nazwę, zmieniam nazwę każdego pliku wyjściowego na rosnącą liczbę całkowitą, jeśli plik („blockname
„) już istnieje:
#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
Oczekiwano trzech plików wyjściowych blockname1
, blockname2
i blockname1_1
(zwróć uwagę, jak ostatni plik ma przypisaną liczbę całkowitą)
#cat blockname1 DATA blockname1 data1 data1 END
(pozostałe odpowiednio …)
Teraz następujący skrypt działa tak, jak chcę:
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 problem dotyczy pętli while
i jego wywołanie systemowe:
Spodziewałem się, że system("test -e " file)
będzie PRAWDA, gdy file
istnieje i będzie FALSE, jeśli file
jeszcze nie istnieje, tzn. pętla while
uruchamia się tylko wtedy, gdy file
jest obecny i złamać, jeśli (nowy) file
jeszcze nie istnieje.
Jednak jeśli użyję system("test -e " file)
(i spraw, aby była rozwlekła za pomocą print file
), mam nieskończoną pętlę o tej samej nazwie z rosnącym przyrostkiem liczby całkowitej i przeciwną system("test !-e " file)
daje pożądany wynik.
Więc zachowuje się dokładnie odwrotnie do tego, czego się spodziewałem.
Odpowiedź
OK, pomyślałem: problem leży w różnych definicjach tego, co jest PRAWDA i FAŁSZ, między statusem wyjścia test
a while
warunek pętli w awk
.
Pozytywne polecenie test
skutkuje kodem zakończenia 0
dla PRAWDA i ujemny w 1
dla FALSE.
Jednak w awk
pętla while
interpretuje 0
jako FALSE i 1
jako TRUE, więc dokładnie odwrotnie definicja.
Jako przykład:
awk "{ while ( 0 ) ; { print "0" } }" file
nie będzie pro duce dowolnego wyjścia, podczas gdy
awk "{ while (1) ; { print "1" } }" file
wypisze nieskończone 1
s.
Najlepsza praktyka należy zatem wyraźnie zaznaczyć w takiej kombinacji
while ( system("command") == 0 )
lub
while ( system("command") == 1 )
.
W moim przypadku
while ( system("test -e " file ) == 0 )
pokazuje oczekiwane zachowanie.
Odpowiedź
awk
system()
zwraca kod zakończenia wykonywanego polecenia – 0 dla powodzenia i! = 0 jeśli nie powodzenie. Na przykład możesz spróbować uruchomić:
v = system("date");
v będzie 0
jeśli uruchomisz:
v może być 127 lub wartością inną niż 0, błąd zwrócony z systemu operacyjnego, jeśli brakuje polecenia dat lub nie znaleziono go.
Odpowiedź
Jeśli rozumiem, celem jest wyodrębnienie zawartości pliku input.file do różnych plików, aby uniknąć utraty bloków o tej samej nazwie.
Jeśli tak przypadku, a jeśli katalog docelowy jest zawsze pusty przed wyodrębnieniem, to jest lepsze (i szybsze) rozwiązanie:
awk " /DATA/{ block=$2; n = blocks[block]++; file=block (n? "_" n: ""); } /DATA/,/END/{ print > file }" input.file
W ten sposób awk nie „Nie trzeba uruchamiać nowej powłoki N razy tylko po to, aby sprawdzić, czy plik istnieje.
Uwagi:
- Nie ma potrzeby bloku BEGIN, ponieważ separator pól awk jest już spacja.
- Nie ma potrzeby umieszczania
"\"
na końcu linii, ponieważ pojedynczy cudzysłów jest już wielowierszowy.
while
. Jednak twoje rozwiązanie jest całkiem zgrabne – dziękuję, wolę to od mojej nieporęcznej rzeczy. Blok BEGIN jest pozostałością z mojego innego formatu pliku – zapomniałem, że w moim ogólnym przykładzie staje się bezużyteczny. Pomijanie odwrotnego ukośnika oszczędzi mi trochę kłopotów. Dzięki za to też. Ale jesteś bezbłędny, jeśli chodzi o zamiar ' skryptu. Czy mógłbyś wyjaśnić częśćn = blocks[block]++; file=block (n? "_" n: "")
bardziej szczegółowo?blocks
, która jest indeksowana według nazwy bloku. Dawny. W pierwszym przypadku"blockname1"
:blocks["blockname1"]
. Awk znajduje ten indeks, a ponieważ nie jest ' znaleziony, zakłada""
(również uważany za zero). Teraz w awkn = var++
jest odpowiednikiem{n=var;var++}
, więcn==""
iblocks["blockname1"]==1
.Wreszciefile=block (n? "_" n: "")
to to samo co{file=block;if(n!="") file+="_" n}
.