Tengo un problema extraño con archivos grandes y bash
. Este es el contexto:
- Tengo un archivo grande: 75G y más de 400,000,000 líneas (es un archivo de registro, lo malo, lo dejé crecer).
- Los primeros 10 caracteres de cada línea son marcas de tiempo en el formato AAAA-MM-DD.
- Quiero dividir ese archivo: un archivo por día.
Intenté con el siguiente script que no funcionó. Mi pregunta es sobre este script que no funciona, no soluciones alternativas .
while read line; do
new_file=${line:0:10}_file.log
echo "$line" >> $new_file
done < file.log
Después de la depuración, encontré el problema en la new_file
variable. Este guión:
while read line; do
new_file=${line:0:10}_file.log
echo $new_file
done < file.log | uniq -c
da el resultado a continuación (pongo los x
es para mantener la confidencialidad de los datos, otros caracteres son los reales). Observe las dh
cadenas más cortas y:
...
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
...
No es un problema en el formato de mi archivo . El guión cut -c 1-10 file.log | uniq -c
solo proporciona marcas de tiempo válidas. Curiosamente, una parte de la salida anterior se convierte en cut ... | uniq -c
:
3227382 2011-xx-x9
4474604 2011-xx-x0
5722027 2011-xx-x1
Podemos ver que después del conteo uniq 4474604
, mi script inicial falló.
¿Llegué a un límite en bash que no conozco, encontré un error en bash (parece improbable) o hice algo mal?
Actualización :
El problema ocurre después de leer 2G del archivo. Las costuras read
y la redirección no les gustan los archivos más grandes que 2G. Pero aún buscando una explicación más precisa.
Actualización2 :
Definitivamente parece un error. Se puede reproducir con:
yes "0123456789abcdefghijklmnopqrs" | head -n 100000000 > file
while read line; do file=${line:0:10}; echo $file; done < file | uniq -c
pero esto funciona bien como una solución alternativa (parece que encontré un uso útil cat
):
cat file | while read line; do file=${line:0:10}; echo $file; done | uniq -c
Se ha archivado un error en GNU y Debian. Las versiones afectadas son bash
4.1.5 en Debian Squeeze 6.0.2 y 6.0.4.
echo ${BASH_VERSINFO[@]}
4 1 5 1 release x86_64-pc-linux-gnu
Actualización3:
Gracias a Andreas Schwab, que reaccionó rápidamente a mi informe de error, este es el parche que es la solución a este mal comportamiento. El archivo afectado es lib/sh/zread.c
como Gilles señaló 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;
La r
variable se utiliza para mantener el valor de retorno de lseek
. Como lseek
devuelve el desplazamiento desde el comienzo del archivo, cuando supera los 2 GB, el int
valor es negativo, lo que hace que la prueba if (r >= 0)
falle donde debería haber tenido éxito.
read
declaración en bash.