Encuentra archivos y tarlos (con espacios)

110

Muy bien, problema tan simple aquí. Estoy trabajando en un código de respaldo simple. Funciona bien, excepto si los archivos tienen espacios. Así es como busco archivos y los agrego a un archivo tar:

find . -type f | xargs tar -czvf backup.tar.gz 

El problema es cuando el archivo tiene un espacio en el nombre porque tar piensa que es una carpeta. Básicamente, ¿hay alguna forma de que pueda agregar citas alrededor de los resultados de la búsqueda? ¿O una forma diferente de solucionar este problema?

Caleb Kester
fuente
12
La mejor forma de uso find ... | xargs ...es utilizar el / -0 parámetro en cada -print0: find -print0 ... | xargs -0 .... Esto hará que los nombres de archivo estén separados por un carácter nulo, lo que significa que puede tener espacios o nuevas líneas u otras cosas raras en sus nombres de archivo y seguirá funcionando.
Porges
8
Existe un problema con el uso de xargs y tar de esta manera cuando tiene una gran cantidad de archivos, xargs invocará repetidamente tar -c, y eso seguirá sobrescribiendo su archivo, y el resultado es que no tendrá todos los archivos que espera . Vea esta explicación más detallada y mi respuesta a continuación.
Steve Kehlet

Respuestas:

217

Utilizar este:

find . -type f -print0 | tar -czvf backup.tar.gz --null -T -

Va a:

  • lidiar con archivos con espacios, líneas nuevas, guiones iniciales y otras cosas divertidas
  • manejar un número ilimitado de archivos
  • no sobrescribirá repetidamente su backup.tar.gz como usar tar -ccon xargslo hará cuando tenga una gran cantidad de archivos

Ver también:

Steve Kehlet
fuente
1
¿Cómo harías esto si primero quisieras canalizar tu hallazgo a través de sed varias veces? por ejemplo, encontrar. -print0 | sed / backups / d | tar ....
Brad Parks
8
Tenga en cuenta que si tiene varias condiciones, debe agregar paréntesis. De lo contrario, se -print0aplica solo a la última expresión. Por ejemplofind . \( -type f -o -name '*.c' \) -print0 | ...
nimrodm
1
Para divertirse, aquí hay una versión de Windows de esto usando cygwin:c:\cygwin\bin\find . -regextype posix-egrep -regex '.*(sln^|vcxproj^|filters)$' -print0 | c:\cygwin\bin\tar -cvf MS_Projects.tar --null -T -
Jon
1
@ Steve, ¿puedes explicar qué es la opción '-' al final del comando tar? No puedo encontrarlo en la página de manual de GNU tar.
shaffooo
Por supuesto, es un parámetro para -T, y significa leer los nombres de archivo de la entrada estándar: si le da un solo guión como nombre de archivo para `--files-from ', (es decir, especifica --files-from = - o -T -), los nombres de los archivos se leen desde la entrada estándar
Steve Kehlet
14

Podría haber otra forma de lograr lo que desea. Básicamente,

  1. Utilice el comando de búsqueda para generar la ruta a los archivos que esté buscando. Redirigir stdout a un nombre de archivo de su elección.
  2. Luego tar con la opción -T que le permite tomar una lista de ubicaciones de archivos (¡la que acaba de crear con buscar!)

    find . -name "*.whatever" > yourListOfFiles
    tar -cvf yourfile.tar -T yourListOfFiles
    
propenso a errores
fuente
Hay una respuesta aquí sobre cómo manejar nombres de archivo con nuevas líneas en ellas: superuser.com/a/513319/151261
tommy.carstensen
8

Intenta ejecutar:

    find . -type f | xargs -d "\n" tar -czvf backup.tar.gz 
gsteff
fuente
7

Por qué no:

tar czvf backup.tar.gz *

Seguro que es inteligente usar find y luego xargs, pero lo estás haciendo de la manera difícil.

Actualización: Porges ha comentado con una opción de búsqueda que creo que es una mejor respuesta que mi respuesta, o la otra: find -print0 ... | xargs -0 ....

Warren P
fuente
Mi código completo solo respaldará los elementos que se modificaron el día anterior. Como es una copia de seguridad diaria, no quiero tener información repetida para guardar en el tamaño del archivo (también tengo una copia de seguridad completa cada 15 días).
Caleb Kester
Para hacer de esto una mejor pregunta SO, haría la pregunta sobre "usar de manera confiable find, xargs y tar juntos". Su título y pregunta no especifican realmente que necesita buscar y xargs, y aún así lo hace.
Warren P
xargs ... tar c ...sobrescribirá el primer archivo creado si la lista de archivos es demasiado larga y xargsse ejecutará tarpor segunda vez. Para evitar la sobrescritura, puede utilizar, xargs -xpero el archivo podría estar incompleto. La alternativa podría ser primero tar c ...y luego posiblemente repetidamente tar r .... (mi contribución a la fiabilidad :)
pabouk
3

Si tiene varios archivos o directorios y desea comprimirlos en un *.gzarchivo independiente , puede hacerlo. Opcional-type f -atime

find -name "httpd-log*.txt" -type f -mtime +1 -exec tar -vzcf {}.gz {} \;

Esto comprimirá

httpd-log01.txt
httpd-log02.txt

a

httpd-log01.txt.gz
httpd-log02.txt.gz
Kalibur x
fuente
2

¿Por qué no probar algo como esto? tar cvf scala.tar `find src -name *.scala`

Frank Eggink
fuente
2

Otra solución como se ve aquí :

find var/log/ -iname "anaconda.*" -exec tar -cvzf file.tar.gz {} +
tommy.carstensen
fuente
2

Agregaría un comentario a la publicación de @Steve Kehlet pero necesito 50 repeticiones (RIP).

Para cualquiera que haya encontrado esta publicación a través de numerosas búsquedas en Google, encontré una manera no solo de encontrar archivos específicos dado un rango de tiempo, sino también de NO incluir las rutas relativas O los espacios en blanco que causarían errores de tarificación. (MUCHAS GRACIAS STEVE.)

find . -name "*.pdf" -type f -mtime 0 -printf "%f\0" | tar -czvf /dir/zip.tar.gz --null -T -
  1. . directorio relativo

  2. -name "*.pdf" busque archivos PDF (o cualquier tipo de archivo)

  3. -type f tipo para buscar es un archivo

  4. -mtime 0 buscar archivos creados en las últimas 24 horas

  5. -printf "%f\0"Regular -print0O -printf "%f"NO funcionó para mí. De las páginas man:

Esta cita se realiza de la misma forma que para GNU ls. Este no es el mismo mecanismo de cotización que el utilizado para -ls y -fls. Si puede decidir qué formato utilizar para la salida de búsqueda, normalmente es mejor utilizar '\ 0' como terminador que utilizar una nueva línea, ya que los nombres de archivo pueden contener espacios en blanco y caracteres de nueva línea.

  1. -czvf crear archivo, filtrar el archivo a través de gzip, enumerar detalladamente los archivos procesados, nombre del archivo

Editar 2019-08-14: Me gustaría agregar que también pude usar esencialmente el mismo comando en mi comentario, solo usando tar en sí:

tar -czvf /archiveDir/test.tar.gz --newer-mtime=0 --ignore-failed-read *.pdf

Necesario --ignore-failed-readen caso de que no haya nuevos PDF para hoy.

usuario3472383
fuente
1

La mejor solución parece ser crear una lista de archivos y luego archivarlos porque puede usar otras fuentes y hacer algo más con la lista.

Por ejemplo, esto permite usar la lista para calcular el tamaño de los archivos que se archivan:

#!/bin/sh

backupFileName="backup-big-$(date +"%Y%m%d-%H%M")"
backupRoot="/var/www"
backupOutPath=""

archivePath=$backupOutPath$backupFileName.tar.gz
listOfFilesPath=$backupOutPath$backupFileName.filelist

#
# Make a list of files/directories to archive
#
echo "" > $listOfFilesPath
echo "${backupRoot}/uploads" >> $listOfFilesPath
echo "${backupRoot}/extra/user/data" >> $listOfFilesPath
find "${backupRoot}/drupal_root/sites/" -name "files" -type d >> $listOfFilesPath

#
# Size calculation
#
sizeForProgress=`
cat $listOfFilesPath | while read nextFile;do
    if [ ! -z "$nextFile" ]; then
        du -sb "$nextFile"
    fi
done | awk '{size+=$1} END {print size}'
`

#
# Archive with progress
#
## simple with dump of all files currently archived
#tar -czvf $archivePath -T $listOfFilesPath
## progress bar
sizeForShow=$(($sizeForProgress/1024/1024))
echo -e "\nRunning backup [source files are $sizeForShow MiB]\n"
tar -cPp -T $listOfFilesPath | pv -s $sizeForProgress | gzip > $archivePath
Nux
fuente
¿Un trazador de líneas para esto?
Robino