Cómo combinar el comando 'tar' con 'find'

31

El comando find da esta salida:

[root @ localhost /] # find var / log / -iname anaconda. *
var / log / anaconda.log
var / log / anaconda.xlog
var / log / anaconda.yum.log
var / log / anaconda.syslog
var / log / anaconda.program.log
var / log / anaconda.storage.log

Después de combinar con tar, muestra esta salida:

[root @ localhost /] # find var / log / -iname anaconda. * -exec tar -cvf file.tar {} \;
var / log / anaconda.log
var / log / anaconda.xlog
var / log / anaconda.yum.log
var / log / anaconda.syslog
var / log / anaconda.program.log
var / log / anaconda.storage.log

Pero al enumerar el archivo tar, solo muestra un único archivo

[root @ localhost /] # tar -tvf file.tar
-rw ------- root / root 208454 2012-02-27 12:01 var / log / anaconda.storage.log

¿Qué estoy haciendo mal aquí?

Con xargs obtengo esta salida:

[root @ localhost /] # find var / log / -iname anaconda. * | xargs tar -cvf file1.tar

Segunda pregunta

Mientras escribe / delante de var, significa find /var/logpor qué le está dando este tar de mesaage : eliminando los principales '/' de los nombres de miembros

[root @ localhost /] # find / var / log / -iname anaconda. * -exec tar -cvf file.tar {} \;
tar: eliminando los principales '/' de los nombres de miembros
/var/log/anaconda.log
tar: eliminando los principales '/' de los nombres de miembros
/var/log/anaconda.xlog
tar: eliminando los principales '/' de los nombres de miembros
/var/log/anaconda.yum.log
tar: eliminando los principales '/' de los nombres de miembros
/var/log/anaconda.syslog
tar: eliminando los principales '/' de los nombres de miembros
/var/log/anaconda.program.log
tar: eliminando los principales '/' de los nombres de miembros
/var/log/anaconda.storage.log

En una forma simple, ¿cuál es la diferencia entre los dos siguientes?

find var/log y find /var/log

max
fuente
Este es un tema semi + fuera, pero en adelante con el findcomando, debe citar el término de búsqueda. Funciona sin a veces pero no siempre.
nerdwaller
1
Si lo usa en {} +lugar de {} \;eso, agrupará los resultados de find en un argumento
Jason S

Respuestas:

39

Nota: Vea la respuesta de @ Iain para una solución algo más eficiente.

Tenga en cuenta que findllamará a la -execacción para cada archivo que encuentre.

Si ejecuta tar -cvf file.tar {}para cada findsalida de archivo individual , esto significa que sobrescribirá file.tarcada vez, lo que explica por qué termina con un archivo restante que solo contiene anaconda.storage.log: son las últimas findsalidas de archivo .

Ahora, realmente desea agregar los archivos al archivo en lugar de crearlo cada vez (esto es lo que hace la -copción). Entonces, use lo siguiente:

find var/log/ -iname "anaconda.*" -exec tar -rvf file.tar {} \;

La -ropción se agrega al archivo en lugar de recrearlo cada vez.

Nota: Reemplace -iname anaconda.*con -iname "anaconda.*". El asterisco es un comodín y su caparazón puede expandirlofind incluso antes de verlo. Para evitar esta expansión, envuelva el argumento entre comillas dobles.


En cuanto a tareliminar el encabezado /: el archivo solo debe contener nombres de archivo relativos . Si agrega archivos con un encabezado /, se almacenarán como nombres de archivo absolutos , es decir, literalmente /var/…en su computadora, por ejemplo.

IIRC esto es simplemente una precaución para tarimplementaciones que no sean GNU, y es más seguro de esta manera porque no sobrescribirá sus datos reales /var/…cuando extraiga el archivo si contiene nombres de archivo relativos.

slhck
fuente
66
Pero tenga en cuenta que si intenta tarhacer un archivo de cinta real de esta manera, agregando un archivo a la vez, rebobinando la cinta y volviendo a leer todo cada vez para llegar al final, todo sería ridículamente lento. Su solución solo es adecuada si está escribiendo el archivo tar en el disco.
Nicole Hamilton
2
Es cierto, pero creo que podemos ignorar con seguridad esta situación;)
slhck
@slhck * es un comodín que debería coincidir con todas las posibilidades, ¿verdad? pero aquí find /var/log/ -iname anaconda*dando nada y find /var/log/ -iname anaconda.*dando la salida, ¿por qué?
max
Cuando se consume un comodín, ya no se verá findmás. Entonces, si tiene anaconda*, y en su carpeta actual hay algo llamado, por ejemplo, anaconda5(que coincide con este comodín), el comodín se expandirá y findverá en -iname anaconda5lugar de -iname anaconda*. El motivo por el cual el primero no funciona y el segundo sí depende de qué archivos estén en su directorio actual. @max
slhck
2
Puede usar en {} +lugar de {} \;para agrupar los resultados de encontrar en un argumento
Jason S
41

Puedes usar algo como:

find var/log -iname 'anaconda.*' -print0 | tar -cvf somefile.tar --null -T -

El -print0y -Ttrabajar juntos para permitir nombres de archivo con espacios de nueva línea, etc. El final -le dice a tar que lea los nombres de archivo de entrada desde stdin.

Tenga en cuenta que -print0debe aparecer al final de su declaración, según esta respuesta . De lo contrario, probablemente obtendrá más archivos de los que esperaba.

Peter Mortensen
fuente
2
Ha omitido la -nameopción, causando su solución a tartodo el directorio. Si eso es lo que quieres, puedes hacerlo más fácilmente tar -cvf file.tar var/logsin usarlo find.
Nicole Hamilton
2
Hacer +1 en la lista tares una buena idea. Definitivamente es la mejor solución si espera que los nombres de las rutas puedan tener espacios. Incluso lo describiría como el mejor técnicamente, ya que es confiable y eficiente. Pero requiere un conocimiento especial adicional de ambos findy tar. Prefiero la sustitución de comandos prácticamente solo porque es una herramienta más general: aprenda a usarla una vez, luego úsela en todas partes. (Pero lo reconozco, estoy en Windows con un shell donde siempre funciona). Disculpas si parecía grosero.
Nicole Hamilton
2
Ya tienes tu +1. Sea feliz. :) Las líneas de comando largas son siempre la ruina de la creación de procesos i / f en cualquier sistema operativo. Recuerdo haber discutido con Mark Lucovsky en Microsoft a principios de los 90 que su límite de caracteres Unicode de 32K en NT era demasiado pequeño y que se quejara de que no tenía idea de cuántos bytes más se necesitarían para almacenar longitudes tan largas en lugar de cortos en todas partes del núcleo . Suspiro. Las soluciones de casos más generales cuando la lista arg es demasiado larga son hacer más en el shell (si es posible; en el mío) o usar xargs.
Nicole Hamilton
99
si usa la -print0opción find , también necesita la --nullopción tar .
mivk
2
Y --no-unquoteresulta que también es necesario: los nombres de archivo que contengan barras invertidas se manejarían de forma incorrecta. (No, esto no es hipotético: realmente estoy creando un archivo tar a partir del código de otra persona, que contiene un nombre de archivo con barras invertidas en el nombre, así es como lo descubrí.)
hvd
12

Prueba esto:

tar -cvf file.tar `find var/log/ -iname "anaconda.*"`

Que estaba tratando de usar finda -exec tar. Pero la forma en que funciona la -execopción, ejecuta ese comando una vez para cada archivo coincidente que encuentra, haciendo tarque se sobrescriba el archivo tar que produce cada vez. Es por eso que solo terminaste con el último. Además, debe poner comillas alrededor del patrón que especifique para findque el shell no lo expanda antes de pasarlo find.

Usando la sustitución de comandos con backticks (o usando la $(...)notación si lo prefiere), la lista completa de nombres producidos por findse vuelve a pegar en la línea de comandos como argumentos tar, lo que hace que los escriba todos a la vez.

Nicole Hamilton
fuente
2
Esto podría terminar mal si encuentra archivos de salida con espacios en su nombre, líneas nuevas o caracteres globales. Esto está destinado a fallar: la tubería stdout de findrara vez es una buena idea. mywiki.wooledge.org/ParsingLs
slhck
3
@slhck, canalizar stdout de find es, de hecho, una buena idea, como se explica muy claramente en la página a la que enlazaste en tu comentario :). De hecho, es la forma recomendada de hacer las cosas. Deberías usar algunos trucos (como el read -rde -print0) como hice en mi respuesta.
terdon
44
@slhck Esta es la razón por la cual los nombres de archivos y directorios en Unix y Linux han evitado tradicionalmente los espacios en los nombres. También es la razón por la cual, en Windows, donde los nombres con espacios son comunes, agregué una notación de sustitución de comando adicional a mi propio shell de Hamilton C usando dobles teclas de retroceso que tratan las líneas completas (posiblemente incluyendo espacios) como palabras individuales para pegarlas nuevamente en el comando línea. Desafortunadamente, ninguno de los shells de Unix tiene esa característica.
Nicole Hamilton
1
Es posible que tradicionalmente lo hayan evitado, pero con los archivos que se crean en el espacio del usuario a través de GUI, ya no puede descuidar los archivos con espacios y tratarlos como ciudadanos de segunda clase (solo porque es Unix). Es bueno que lo hayas incluido en tu shell, pero es para Windows, y los shells de Unix no necesitan particularmente esa característica si simplemente usas la sintaxis correcta y tomas las precauciones adecuadas. Por eso publiqué mi comentario en primer lugar.
slhck
2
No, pero en otros lugares bien podría suceder. Es por eso que es una buena idea programar a la defensiva: más vale prevenir que curar. Además, los visitantes que encuentran esta pregunta no necesariamente tienen el mismo problema y se preguntan por qué el comando que encontraron aquí pareció funcionar para este mismo caso, pero falló para ellos. Dejaré que corrija el comando, solo pensé que era importante mencionarlo porque muchas personas se topan con este problema tarde o temprano.
slhck
6

Pregunta 1

Su comando falla porque tarestá tomando cada uno de los archivos encontrados y archivándolos file.tar. Cada vez que lo haga, sobrescribirá el creado anteriormente file.tar.

Si lo que desea es un archivo con todos los archivos, simplemente ejecútelo tardirectamente, no es necesario find(y sí, esto funciona para archivos con espacios en sus nombres):

tar -vcf file.tar /var/log/anaconda*   

Pregunta 2

Los dos comandos son completamente diferentes:

  • find var / log buscará un directorio llamado var/log que es un subdirectorio de su directorio actual , es equivalente a find ./var/log(observe el ./).

  • find / var / log buscará un directorio llamado /var/log que es un subdirectorio de la raíz,/ .

El /mensaje principal es de tar, no find. Significa que está eliminando el primero /de sus nombres de archivo para convertir las rutas absolutas en relativas . Esto significa que el archivo /var/log/anaconda.errorse extraerá ./var/log/anaconda.errorcuando desempaquete el archivo.

terdon
fuente
1

Hay dos formas de -exectrabajar. Una forma ejecuta el comando muchas veces, una vez para cada archivo; la otra forma ejecuta el comando una vez, incluidos todos los archivos como una lista de parámetros.

  • -exec tar -cvf file.tar {} ';'ejecuta el tarcomando para cada archivo, sobrescribiendo el archivo cada vez.
  • -exec tar -cvf file.tar {} '+'ejecuta el tarcomando una vez, creando un archivo de todos los archivos encontrados.
mwfearnley
fuente
1

Creo que usar -exec para cada archivo puede hacer que la compresión de tar sea muy lenta, si tienes muchos archivos. Prefiero usar el comando:

find . -iname "*.jpg" | cpio -ov -H tar -F jpgs.tar
fabceolin
fuente
hasta que comience a fallar/bin/cpio: xxx: Cannot open: Too many open files
SYN