У меня странная проблема с большими файлами и bash. Это контекст:
- У меня большой файл: 75G и 400,000,000+ строк (это файл журнала, мой плохой, я позволил ему расти).
- Первые 10 символов каждой строки представляют собой метки времени в формате ГГГГ-ММ-ДД.
- Я хочу разделить этот файл: один файл в день.
Я попытался с помощью следующего сценария, который не работал. Мой вопрос о том, что этот скрипт не работает, а не альтернативные решения .
while read line; do
new_file=${line:0:10}_file.log
echo "$line" >> $new_file
done < file.log
После отладки я нашел проблему в new_fileпеременной. Этот скрипт:
while read line; do
new_file=${line:0:10}_file.log
echo $new_file
done < file.log | uniq -c
дает результат ниже (я ставлю xes, чтобы сохранить конфиденциальность данных, другие символы являются реальными). Обратите внимание на dhи более короткие строки:
...
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
...
Это не проблема в формате моего файла . Скрипт cut -c 1-10 file.log | uniq -cвыдает только допустимые метки времени. Интересно, что часть вышеприведенного вывода делается с cut ... | uniq -c:
3227382 2011-xx-x9
4474604 2011-xx-x0
5722027 2011-xx-x1
Мы видим, что после подсчета uniq 4474604мой первоначальный скрипт не удался.
Я достиг предела в bash, которого я не знаю, обнаружил ли я ошибку в bash (она кажется маловероятной), или я сделал что-то не так?
Обновление :
Проблема возникает после прочтения 2G файла. Это швы readи перенаправление не любят большие файлы, чем 2G. Но все еще в поисках более точного объяснения.
Обновление 2 :
Это определенно выглядит как ошибка. Это может быть воспроизведено с:
yes "0123456789abcdefghijklmnopqrs" | head -n 100000000 > file
while read line; do file=${line:0:10}; echo $file; done < file | uniq -c
но это прекрасно работает в качестве обходного пути (кажется, что я нашел полезное применение cat):
cat file | while read line; do file=${line:0:10}; echo $file; done | uniq -c
Ошибка была подана в GNU и Debian. Подвержены уязвимости версии bash4.1.5 в Debian Squeeze 6.0.2 и 6.0.4.
echo ${BASH_VERSINFO[@]}
4 1 5 1 release x86_64-pc-linux-gnu
Update3:
Благодаря Андреасу Швабу, который быстро отреагировал на мое сообщение об ошибке, этот патч является решением этой проблемы. Файл, на который повлияли, - это, lib/sh/zread.cкак Жиль указал раньше:
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;
rПеременная используется для хранения возвращаемого значения lseek. As lseekвозвращает смещение от начала файла, когда оно превышает 2 ГБ, intзначение является отрицательным, что приводит if (r >= 0)к сбою теста там, где он должен был быть успешным.
readутверждения в bash.