2010-04-23 19 views
21

Tengo un trabajo que se ejecuta en mi servidor en el indicador de línea de comandos durante dos días:manera más eficiente para encontrar y alquitrán millones de archivos

find data/ -name filepattern-*2009* -exec tar uf 2009.tar {} ; 

Es tomar siempre, y algo más. Sí, hay millones de archivos en el directorio de destino. (Cada archivo es un mísero 8 bytes en una estructura de directorios así hash.) Pero sólo corriendo ...

find data/ -name filepattern-*2009* -print > filesOfInterest.txt 

... tan sólo dos horas más o menos. A la velocidad en que mi trabajo se está ejecutando, no estará terminado por un par de semanas .. Eso parece irrazonable. ¿Hay una forma más eficiente de hacer esto? ¿Quizás con un script bash más complicado?

A preguntas secundarias es "¿Por qué mi enfoque actual es tan lenta?"

+6

El parámetro Exec genera un nuevo proceso de alquitrán para cada archivo encontrado. Además, la operación de actualización tar es costosa. – theomega

+0

¿cuántos archivos tienes y qué tan grandes son? El impacto de invocar tar para cada uno de los archivos hace una gran diferencia para 10 archivos petabyte o 10 billones de archivos pequeños – sfussenegger

+0

@sfussenegger: 8 bytes cada uno ... tiene una pregunta actualizada. Como se indica en q, hay * millones * de archivos. –

Respuesta

19

Si ya lo hizo el segundo comando que creado la lista de archivos, solo use la opción -T para decirle a tar que lea los nombres de los archivos de esa lista de archivos guardados. Ejecutar 1 comando tar contra N tar será mucho mejor.

+0

Después de correr con 'xargs' por un tiempo, probé este enfoque ... ¡y fue ** mucho ** más rápido! –

+2

tenga cuidado con 'xargs' en esta situación: si se le pasan muchos nombres de archivo, ejecuta' tar' varias veces en subconjuntos de la lista de archivos. En su caso, con 'tar -u' que probablemente funcione, pero si está creando un archivo tar' tar -c', solo el último subconjunto de archivos estará ahí una vez que haya terminado ... – drevicko

7

Hay xargs para esto:

find data/ -name filepattern-*2009* -print0 | xargs -0 tar uf 2009.tar 

Adivinar por qué es lento es difícil ya que no hay mucha información. ¿Cuál es la estructura del directorio, qué sistema de archivos usa, cómo se configuró en la creación? Tener millones de archivos en un solo directorio es una situación bastante difícil para la mayoría de los sistemas de archivos.

+0

El directorio está muy bien organizado. ext3, por cierto. Como mencioné, el comando find solo se ejecuta rápidamente, así que creo que el sistema de archivos, la estructura del directorio, etc. no es un problema. –

+0

Creo que tendrás que agregar '--max-args = n' (corto' -n n') donde 'n' es la cantidad máxima de argumentos que tar (o cualquier otro programa) puede tomar. 'getconf ARG_MAX' debe mostrar qué tan alto es este límite (131,072 en mi máquina). Aunque es posible que xargs se encargue de esto por sí mismo. – sfussenegger

+0

¡Guau! Así que ejecuté otro comando con 'xargs' como dijiste hace 15 minutos, y el archivo tar resultante es ya el 25% del tamaño de mi comando original. Gracias. –

2

La forma en que actualmente tiene cosas, está invocando el comando tar cada vez que encuentra un archivo, lo cual no es sorprendente lento. En lugar de tomarse las dos horas para imprimir más la cantidad de tiempo que lleva abrir el archivo tar, ver si los archivos están desactualizados y agregarlos al archivo, en realidad está multiplicando esos tiempos. Es posible que tenga más éxito al invocar el comando tar una vez, después de haber agrupado todos los nombres, posiblemente usando xargs para lograr la invocación. Por cierto, espero que estés usando 'filepattern- * 2009 *' y no filepattern- * 2009 * ya que las estrellas serán expandidas por el shell sin comillas.

24

Una opción es utilizar cpio para generar un archivo tar-formato:

$ find data/ -name "filepattern-*2009*" | cpio -ov --format=ustar > 2009.tar 

cpio funciona de forma nativa con una lista de nombres de archivo de la entrada estándar, en lugar de un directorio de nivel superior, lo que hace es una herramienta ideal para esta situación.

+2

esta es una solución elegante. y puedes ejecutarlo a través de una red. reemplace '> 2009.tar' con' | ssh host tar xf -' –

+5

'buscar datos/-print0 | tar -T - --null --create -f archive.tar' lee la lista de archivos de stdout y utiliza el delimitador de archivos nulos –

+3

Mi ubuntu tar no desea tener '--null' después de' '' '' '' '. Tuve que usar: 'buscar datos/-print0 | tar --null -T - --create -f archive.tar' –

8

Aquí es una combinación hallazgo de alquitrán que puede hacer lo que quiera sin el uso de xargs o exec (que debería dar lugar a una notable aceleración):

tar --version # tar (GNU tar) 1.14 

# FreeBSD find (on Mac OS X) 
find -x data -name "filepattern-*2009*" -print0 | tar --null --no-recursion -uf 2009.tar --files-from - 

# for GNU find use -xdev instead of -x 
gfind data -xdev -name "filepattern-*2009*" -print0 | tar --null --no-recursion -uf 2009.tar --files-from - 

# added: set permissions via tar 
find -x data -name "filepattern-*2009*" -print0 | \ 
    tar --null --no-recursion --owner=... --group=... --mode=... -uf 2009.tar --files-from - 
3

para controlar correctamente los nombres de archivo con caracteres extraños (pero legales) (tales como los saltos de línea, ...) debe escribir su lista de archivos para filesOfInterest.txt usando -print0 del hallazgo:

find -x data -name "filepattern-*2009*" -print0 > filesOfInterest.txt 
tar --null --no-recursion -uf 2009.tar --files-from filesOfInterest.txt 
-2

más simple (también eliminar el archivo después de la creación de archivo):

find *.1 -exec tar czf '{}.tgz' '{}' --remove-files \; 
+1

Casi no hay diferencia con el enfoque original del asker, que supuestamente era demasiado lento. Además, elimina innecesariamente los archivos de origen, que no se han solicitado y que sin duda no serán deseados. –

Cuestiones relacionadas