Tengo un archivo que crece aproximadamente 200,000 líneas por día, y todo está formado con bloques de tres líneas como tales:
1358726575123 # key
Joseph Muller # name
carpenter # job
9973834728345
Andres Smith
student
7836472098652
Mariah Anthony
dentist
Ahora, tengo otro archivo del que extraigo unos 10,000 patrones clave, como 1358726575123
. Luego ejecuto un for
ciclo con estos patrones y tengo que compararlos con el primer archivo. Si el archivo no contiene dicho patrón, guardo el patrón en un tercer archivo para su posterior procesamiento:
for number in $(grep -o '[0-9]\{12\}' file2); do # finds about 10.000 keys
if ! grep -q ^$number$ file1; then # file1 is a huge file
printf "$number\n" >>file3 # we'll process file3 later
fi
done
El código de ejemplo engloba un archivo enorme 10,000 veces, y ejecuto este bucle aproximadamente una vez por minuto, durante todo el día .
Dado que el gran archivo sigue creciendo, ¿qué puedo hacer para acelerar todo esto y ahorrar algo de CPU? Me pregunto si ordenar el archivo de alguna manera por su clave (si es así, ¿cómo?) O usar un db en lugar de texto sin formato ayudaría ...
Respuestas:
Esta respuesta se basa en la
awk
respuesta publicada por potong .Es el doble de rápido que el
comm
método (en mi sistema), para los mismos 6 millones de líneas en el archivo principal y 10 mil claves ... (ahora actualizado para usar FNR, NR)Aunque
awk
es más rápido que su sistema actual y le dará a usted y a su computadora algo de espacio para respirar, tenga en cuenta que cuando el procesamiento de datos es tan intenso como lo ha descrito, obtendrá los mejores resultados generales al cambiar a una base de datos dedicada; p.ej. SQlite, MySQL ...fuente
file1 -> mainfile
yfile2 -> keys
con gawk y mawk, y produce claves incorrectas.awk
permiten leer en una serie de archivos ... En este caso, esa serie tiene 3 archivos. La salida va astdout
mainfile
, Y también imprimirá cualquier clave delkeys
archivo que NO esté enmainfile
... Eso es probablemente lo que está sucediendo ... (Lo investigaré un poco más ...$RANDOM
para cargar.El problema, por supuesto, es que ejecuta grep en el archivo grande 10,000 veces. Debería leer ambos archivos solo una vez. Si desea permanecer fuera de los lenguajes de secuencias de comandos, puede hacerlo de esta manera:
comm
en las listas ordenadas para obtener lo que solo está en la segunda listaAlgo como esto:
Ver
man comm
.Si pudiera truncar el archivo grande todos los días (como un archivo de registro), podría mantener un caché de números ordenados y no necesitaría analizarlo todo el tiempo.
fuente
{12}
... OP ha usado 12, pero las claves de ejemplo son 13 largas ...<(grep...sort)
dónde están los nombres de los archivos.tail -n +$linenum
para generar solo los últimos datos. De esta forma, solo procesará aproximadamente 200,000 líneas por día. Lo probé con 6 millones de líneas en el archivo principal y 10 mil claves ... tiempo : 0m0.016s reales, usuario 0m0.008s, sys 0m0.008sSí, definitivamente use una base de datos. Están hechos exactamente para tareas como esta.
fuente
Esto podría funcionar para usted:
EDITAR:
La secuencia de comandos modificada para permitir duplicados y claves desconocidas en ambos archivos, aún produce claves del primer archivo que no está presente en el segundo:
fuente
Con esa cantidad de datos, realmente debería cambiar a una base de datos. Mientras tanto, una cosa que debe hacer para acercarse al rendimiento decente es no buscar
file1
cada clave por separado. Ejecute una solagrep
para extraer todas las claves no excluidas a la vez. Dado que esogrep
también devuelve líneas que no contienen una clave, filtrelas.(
-Fx
significa buscar líneas enteras, literalmente.-f -
significa leer una lista de patrones de la entrada estándar).fuente
-v
(-Fxv
) puede encargarse de eso.comm
.Permítame reforzar lo que otros han dicho: "¡Llévelo a una base de datos!"
Hay binarios de MySQL disponibles gratuitamente para la mayoría de las plataformas.
¿Por qué no SQLite? Está basado en la memoria, carga un archivo plano cuando lo inicia y luego lo cierra cuando haya terminado. Esto significa que si su computadora falla o el proceso de SQLite desaparece, también lo hacen todos los datos.
¡Su problema se parece a un par de líneas de SQL y se ejecutará en milisegundos!
Después de instalar MySQL (que recomiendo sobre otras opciones), pagaría $ 40 por el Libro de cocina SQL de O'Reilly de Anthony Molinaro, que tiene muchos patrones de problemas, comenzando con
SELECT * FROM table
consultas simples y pasando por agregados y múltiples combinaciones.fuente
No estoy seguro de si este es el resultado exacto que está buscando, pero probablemente la forma más fácil es:
También puedes usar:
Cada uno de estos crea un archivo de patrón temporal que se utiliza para obtener los números del archivo grande (
file1
).fuente
grep -vf
lugar degrep -f
.Estoy totalmente de acuerdo con que obtenga una base de datos (MySQL es bastante fácil de usar). Hasta que lo ejecute, me gusta la
comm
solución de Angus , pero hay tantas personas que lo intentangrep
y se equivocan que pensé en mostrarle la forma correcta (o al menos una) de hacerlogrep
.El primero
grep
recibe las llaves. El tercerogrep
(en el<(...)
) toma todas las claves utilizadas en el archivo grande, y lo<(...)
pasa como un archivo como argumento-f
en el segundo grep. Eso hace que el segundo logrep
use como una lista de líneas para que coincida. Luego usa esto para hacer coincidir su entrada (la lista de claves) de la tubería (primerogrep
) e imprime cualquier clave extraída del archivo de clave y no (-v
) el archivo grande.Por supuesto, puede hacer esto con archivos temporales que debe controlar y recordar eliminar:
Esto imprime todas las líneas
allkeys
que no aparecen enusedkeys
.fuente
grep: Memory exhausted
comm
, en ese orden.El archivo de claves no cambia? Entonces debe evitar buscar las entradas antiguas una y otra vez.
Con
tail -f
usted puede obtener la salida de un archivo en crecimiento.grep -f lee los patrones de un archivo, una línea como patrón.
fuente
No iba a publicar mi respuesta porque pensé que esa cantidad de datos no debería procesarse con un script de shell, y ya se había dado la respuesta correcta para usar una base de datos. Pero desde ahora hay otros 7 enfoques ...
Lee el primer archivo en la memoria, luego toma el segundo archivo para buscar números y verifica si los valores están almacenados en la memoria. Debería ser más rápido que varios
grep
s, si tiene suficiente memoria para cargar todo el archivo, eso es.fuente
Estoy de acuerdo con @ jan-steinman que debe usar una base de datos para este tipo de tarea. Hay muchas maneras de hackear una solución con un script de shell como muestran las otras respuestas, pero hacerlo de esa manera conducirá a mucha miseria si vas a usar y mantener el código por un período de tiempo más largo que solo un proyecto descartable de un día.
Suponiendo que esté en una caja de Linux, lo más probable es que tenga Python instalado de forma predeterminada, que incluye la biblioteca sqlite3 a partir de Python v2.5. Puede verificar su versión de Python con:
Recomiendo usar la biblioteca sqlite3 porque es una solución simple basada en archivos que existe para todas las plataformas (¡incluso dentro de su navegador web!) Y no requiere la instalación de un servidor. Esencialmente configuración cero y mantenimiento cero.
A continuación se muestra una secuencia de comandos de Python simple que analizará el formato de archivo que proporcionó como ejemplo y luego realiza una consulta simple de "seleccionar todo" y genera todo lo que almacena en la base de datos.
Sí, esto significa que necesitará aprender algo de SQL , pero a la larga valdrá la pena. Además, en lugar de analizar sus archivos de registro, tal vez podría escribir datos directamente en su base de datos sqlite.
fuente
/usr/bin/sqlite3
funciona de la misma manera para los scripts de shell ( packages.debian.org/squeeze/sqlite3 ), aunque nunca lo he usado./usr/bin/sqlite3
con scripts de shell, sin embargo, le recomiendo evitar los scripts de shell, excepto para los programas de descarte simples y en su lugar use un lenguaje como python que tenga un mejor manejo de errores y sea más fácil de mantener y crecer.