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}.