¿Es posible en bash comenzar a leer un archivo desde un desplazamiento de conteo de bytes arbitrario?


22

Quiero localizar una fecha que esté en algún lugar de un registro de 8 GB (texto).

¿Puedo pasar por alto una lectura secuencial completa, y primero hacer divisiones binarias del archivo (tamaño), o navegar de alguna manera por el sistema de archivos inodes(del cual sé muy poco), para comenzar a leer desde cada punto dividido, hasta encontrar un desplazamiento adecuado de ¿Dónde comenzar mi búsqueda de texto para una línea que incluya la fecha?

tailLa lectura de la última línea no usa una lectura secuencial normal, por lo que me pregunto si esta instalación está disponible de alguna manera en bash, o si necesitaría usar Python o C / C ++ ... pero estoy específicamente interesado en una bashopción ..


Respuestas:


8
for (( block = 0; block < 16; block += 1 ))
do 
    echo $block; 
    dd if=INPUTFILE skip=$((block*512))MB bs=64 count=1 status=noxfer 2> /dev/null | \
        head -n 1
done

que ... no crea archivos divididos en temp, omite bloques * 512 MB de datos en cada ejecución, lee 64 bytes desde esa posición y limita la salida a la primera línea de esos 64 bytes.

es posible que desee ajustar 64 a lo que crea que necesita.


@akira ... Esto se ve muy bien, pero quiero verlo un poco más primero ... (entonces, hasta mañana .....
Peter.O

1
@akira .. 'dd' es increíble. Funciona bien con la búsqueda binaria dividida ... Ahora puedo extraer una línea regex'd (por su tecla Fecha), de un archivo 8G ordenado en menos de 1 segundo ... Entonces parece que voy a lograr mi 3 segundo objetivo personal para extraer un rango de fechas entre dos teclas (inclusive) ... excluyendo el tiempo de salida, que varía según la cantidad que se emite ... También lo usaré ddpara eso ... ¡Es una gran herramienta! :)
Peter.O

30

Suena como si quisieras:

tail -c +1048576

o cualquier número de bytes que quieras omitir. El signo más le dice a tail que mida desde el inicio del archivo en lugar del final. Si está utilizando la versión GNU de tail, puede escribir eso como:

tail -c +1M

Para obtener un número fijo de bytes después del corte, en lugar de todo el resto del archivo, solo pásalo por la cabeza:

tail -c +1048576 | head -c 1024

La flexibilidad de Linux / bash es increíble (definitivamente pasé demasiado tiempo cambiando a Linux). Acababa de aceptar la respuesta de akira, pero lo saqué hasta que lo evalúe más a fondo. ddsalta a un byte específico (como lo hace tail), pero es un dolor que codifica alrededor de longitudes de línea desconocidas, y luego una llamada a sed para quitar las líneas parciales iniciales ... Parece que la cola | cabeza puede hacer eso sin dolor (¿tan rápido?) . No entiendo cómo la cabeza puede cerrar el grifo en la cola, pero parece :) Debe ser un caso de: Si la cabeza deja de recibir, la cola deja de enviar (y deja de leer más). Debo ir ... volver mañana.
Peter.O

@ fred.bear: tail/ headtampoco puedo adivinar a ciegas las longitudes de línea. tienes que saltar a la posición x y luego puedes mirar hacia la izquierda o la derecha de x para la siguiente \n. no importa cómo se llame el programa. entonces, en ambos casos saltas a x y luego usas headpara mirar a la derecha para el siguiente final de línea.
akira

tail|headofrece la capacidad de no preocuparse en absoluto por ddla cuenta = val. Con 'dd', si no obtengo suficientes datos, se acabó el juego. La flexibilidad de las longitudes de línea arbitrarias es excelente. He escrito una función para 'dd' que devuelve la línea completa "siguiente más cercana" y su desplazamiento, pero preferiría evitar el problema de la longitud. Ahora he probado tail | head, e inicialmente funciona bien (para compensar = 100 MB), pero se ralentiza drásticamente para tomar 2 minutos para un acceso con desplazamiento = 8 GB (puedo awkhacerlo en 1 minuto) ... así que es genial para archivos más pequeños ... Gracias por
informarme sobre el

2

Intentaría algo como esto para dividir el registro en fragmentos de 512MiB para un análisis más rápido.

split <filename> -b 536870912

Si está buscando el archivo, lo siguiente funcionaría:

for file in x* ; do
  echo $file
  head -n 1 $file
done

Use esa salida para determinar qué archivo grep para su fecha.


Gracias, pero es más lento que una búsqueda secuencial. Echa un vistazo a mis comentarios aquí unix.stackexchange.com/questions/8121/… (en lugar de volver a escribir lo mismo aquí)
Peter.O

Al usar 'dividir', toca cada byte una vez. si haces eso, también podrías obtener los 8 gb completos.
akira

@sifusam ... Quiero hacer una búsqueda binaria dividida (no solo dividir los archivos) en.wikipedia.org/wiki/Binary_search_algorithm ... así que fue una buena respuesta para una pregunta diferente :) ... Gracias por responder ... +1 para ponerte en marcha ...
Peter.O

0

Aquí está mi script, estoy buscando la primera línea donde el primer campo coincide con mi número. Las líneas se ordenan según el primer campo. Utilizo dd para verificar la primera línea de bloques de 128K, luego salto al bloque y realizo una búsqueda. Mejora la eficiencia si el archivo supera los 1M.

Cualquier comentario o corrección es apreciado!

#!/bin/bash

search=$1;
f=$2;

bs=128;

max=$( echo $(du $f | cut -f1)" / $bs" | bc );
block=$max;
for i in $(seq 0 $max); do
 n=$(dd bs=${bs}K skip=$i if=$f 2> /dev/null| head -2 | tail -1 | cut -f1)
 if [ $n -gt $search ]; then
  block=`expr $i - 1` 
  break;
 fi
done; 
dd bs=${bs}K skip=$block if=$f 2> /dev/null| tail -n +2 | awk -v search="$search" '$1==search{print;exit 1;};$1>search{exit 1;};';

* EDITAR * ** grep es mucho más rápido y ACK aún mejor

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.