Eu tenho um problema estranho com arquivos grandes e bash. Este é o contexto:
- Eu tenho um arquivo grande: 75G e mais de 400.000.000 de linhas (é um arquivo de log, que pena, deixei crescer).
- Os 10 primeiros caracteres de cada linha são carimbos de hora no formato AAAA-MM-DD.
- Eu quero dividir esse arquivo: um arquivo por dia.
Eu tentei com o seguinte script que não funcionou. Minha pergunta é sobre esse script não funcionar, não soluções alternativas .
while read line; do
new_file=${line:0:10}_file.log
echo "$line" >> $new_file
done < file.log
Após a depuração, encontrei o problema na new_filevariável Este script:
while read line; do
new_file=${line:0:10}_file.log
echo $new_file
done < file.log | uniq -c
dá o resultado abaixo (coloquei xes para manter os dados confidenciais, outros caracteres são reais). Observe as dhseqüências de caracteres e as mais curtas:
...
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
...
Não é um problema no formato do meu arquivo . O script cut -c 1-10 file.log | uniq -cfornece apenas carimbos de hora válidos. Curiosamente, uma parte da saída acima se torna com cut ... | uniq -c:
3227382 2011-xx-x9
4474604 2011-xx-x0
5722027 2011-xx-x1
Podemos ver que, após a contagem uniq 4474604, meu script inicial falhou.
Eu atingi um limite no bash que não conheço, encontrei um bug no bash (parece improvável) ou fiz algo errado?
Atualização :
O problema ocorre após a leitura de 2G do arquivo. Costuras reade redirecionamento não gostam de arquivos maiores que 2G. Mas ainda procurando uma explicação mais precisa.
Update2 :
Definitivamente, parece um bug. Pode ser reproduzido com:
yes "0123456789abcdefghijklmnopqrs" | head -n 100000000 > file
while read line; do file=${line:0:10}; echo $file; done < file | uniq -c
mas isso funciona bem como uma solução alternativa (parece que eu achei um uso útil cat):
cat file | while read line; do file=${line:0:10}; echo $file; done | uniq -c
Um bug foi arquivado no GNU e Debian. As versões afetadas são bash4.1.5 no Debian Squeeze 6.0.2 e 6.0.4.
echo ${BASH_VERSINFO[@]}
4 1 5 1 release x86_64-pc-linux-gnu
Update3:
Graças a Andreas Schwab, que reagiu rapidamente ao meu relatório de erros, este é o patch que é a solução para esse mau comportamento. O arquivo impactado é lib/sh/zread.ccomo Gilles apontou antes:
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;
A rvariável é usada para armazenar o valor de retorno de lseek. Como lseekretorna o deslocamento desde o início do arquivo, quando ele ultrapassa 2 GB, o intvalor é negativo, o que causa if (r >= 0)falha no teste onde deveria ter êxito.
readdeclaração em bash.