Mam dziwny problem z dużymi plikami i bash. Oto kontekst:
- Mam duży plik: 75G i ponad 400 000 000 linii (to plik dziennika, mój zły, pozwoliłem mu urosnąć).
- Pierwsze 10 znaków każdej linii to znaczniki czasu w formacie RRRR-MM-DD.
- Chcę podzielić ten plik: jeden plik dziennie.
Próbowałem z następującym skryptem, który nie działał. Moje pytanie dotyczy tego, że ten skrypt nie działa, a nie alternatywnych rozwiązań .
while read line; do
new_file=${line:0:10}_file.log
echo "$line" >> $new_file
done < file.log
Po debugowaniu znalazłem problem w new_filezmiennej. Ten skrypt:
while read line; do
new_file=${line:0:10}_file.log
echo $new_file
done < file.log | uniq -c
podaje wynik poniżej (umieszczam te zasady, xaby zachować poufność danych, inne znaki są prawdziwe). Zwróć uwagę na dhi krótsze ciągi:
...
27402 2011-xx-x4
27262 2011-xx-x5
22514 2011-xx-x6
17908 2011-xx-x7
...
3227382 2011-xx-x9
4474604 2011-xx-x0
1557680 2011-xx-x1
1 2011-xx-x2
3 2011-xx-x1
...
12 2011-xx-x1
1 2011-xx-dh
1 2011-xx-x1
1 208--
1 2011-xx-x1
1 2011-xx-dh
1 2011-xx-x1
...
To nie jest problem w formacie mojego pliku . Skrypt cut -c 1-10 file.log | uniq -cpodaje tylko ważne znaczniki czasu. Co ciekawe, część powyższej produkcji otrzymuje postać cut ... | uniq -c:
3227382 2011-xx-x9
4474604 2011-xx-x0
5722027 2011-xx-x1
Widzimy, że po zliczeniu uniq 4474604mój początkowy skrypt nie powiódł się.
Czy osiągnąłem limit w bashu, którego nie znam, czy znalazłem błąd w bashu (wydaje się to mało prawdopodobne), czy też zrobiłem coś złego?
Aktualizacja :
Problem występuje po odczytaniu 2G pliku. Szwy readi przekierowania nie lubią większych plików niż 2G. Ale wciąż szukam dokładniejszych wyjaśnień.
Aktualizacja 2 :
To definitywnie wygląda jak błąd. Można go powielać za pomocą:
yes "0123456789abcdefghijklmnopqrs" | head -n 100000000 > file
while read line; do file=${line:0:10}; echo $file; done < file | uniq -c
ale działa to dobrze jako obejście (wydaje się, że znalazłem przydatne zastosowanie cat):
cat file | while read line; do file=${line:0:10}; echo $file; done | uniq -c
Błąd został zgłoszony do GNU i Debiana. Dotknięte wersje to bash4.1.5 w Debian Squeeze 6.0.2 i 6.0.4.
echo ${BASH_VERSINFO[@]}
4 1 5 1 release x86_64-pc-linux-gnu
Aktualizacja 3:
Dzięki Andreasowi Schwabowi, który szybko zareagował na mój raport o błędzie, jest to łatka, która jest rozwiązaniem tego złego zachowania. Dotknięty plik jest, lib/sh/zread.cjak wcześniej zauważył Gilles:
diff --git a/lib/sh/zread.c b/lib/sh/zread.c index 0fd1199..3731a41 100644
--- a/lib/sh/zread.c
+++ b/lib/sh/zread.c @@ -161,7 +161,7 @@ zsyncfd (fd)
int fd; { off_t off;
- int r;
+ off_t r;
off = lused - lind; r = 0;
rZmienna jest używana do przechowywania wartości zwracanej lseek. Gdy lseekzwraca przesunięcie od początku pliku, gdy przekracza 2 GB, intwartość jest ujemna, co powoduje, że test if (r >= 0)kończy się niepowodzeniem w miejscu, w którym powinien się powieść.
readbash w kierunku granicy wyrażenia.