tar --exclude no excluye. ¿Por qué?

71

Tengo esta línea muy simple en un script bash que se ejecuta con éxito (es decir, produce el _data.tararchivo), excepto que no excluye los subdirectorios que se le dice excluir mediante la --excludeopción:

/bin/tar -cf /home/_data.tar  --exclude='/data/sub1/*'  --exclude='/data/sub2/*' --exclude='/data/sub3/*'  --exclude='/data/sub4/*'  --exclude='/data/sub5/*'  /data

En su lugar, produce un _data.tararchivo que contiene todo lo que se encuentra debajo de / data, incluidos los archivos en los subdirectorios que quería excluir.

¿Alguna idea de por qué? y como arreglar esto?

Actualización Implementé mis observaciones basadas en el enlace provisto en la primera respuesta a continuación (dir de nivel superior primero, sin espacios en blanco después de la última exclusión):

/bin/tar -cf /home/_data.tar  /data  --exclude='/data/sub1/*'  --exclude='/data/sub2/*'  --exclude='/data/sub3/*'  --exclude='/data/sub4/*'  --exclude='/data/sub5/*'

Pero eso no ayudó. Todos los subdirectorios "excluidos" están presentes en el _data.tararchivo resultante .

Esto es desconcertante. Si esto es un error en el tar actual (GNU tar 1.23, en un CentOS 6.2, Linux 2.6.32) o "extrema sensibilidad" del tar a espacios en blanco y otros errores tipográficos fáciles de omitir, considero que esto es un error. Por ahora.

Esto es horrible : probé la información sugerida a continuación (sin seguimiento /*) y todavía no funciona en el script de producción:

/bin/tar -cf /home/_data.tar  /data  --exclude='/data/sub1'  --exclude='/data/sub2'  --exclude='/data/sub3'  --exclude='/data/sub4'

No puedo ver ninguna diferencia entre lo que probé y lo que intentó @Richard Perrin, excepto por las comillas y 2 espacios en lugar de 1. Voy a intentar esto (debo esperar a que el script nocturno se ejecute como el directorio que se respaldará) arriba es enorme) e informar de nuevo.

/bin/tar -cf /home/_data.tar  /data --exclude=/data/sub1 --exclude=/data/sub2 --exclude=/data/sub3 --exclude=/data/sub4

Estoy empezando a pensar que todas estas tar --excludesensibilidades no son alquitranes sino algo en mi entorno, pero ¿qué podría ser eso?

¡Funcionó! La última variación probada (sin comillas simples y espacio simple en lugar de espacio doble entre las --excludes) probó el trabajo. Extraño pero aceptable.

¡Increíble! Resulta que una versión anterior de tar(1.15.1) solo excluiría si el directorio de nivel superior es el último en la línea de comando. Esto es exactamente lo contrario de lo que requiere la versión 1.23. FYI.

ateiob
fuente

Respuestas:

50

Si desea excluir un directorio completo, su patrón debe coincidir con ese directorio, no con los archivos que contiene. Usar en --exclude=/data/sub1lugar de--exclude='/data/sub1/*'

Tenga cuidado al citar los patrones para protegerlos de la expansión del shell.

Vea este ejemplo, con problemas en la invocación final:

$ for i in 0 1 2; do mkdir -p /tmp/data/sub$i; echo foo > /tmp/data/sub$i/foo; done
$ find /tmp/data
/tmp/data
/tmp/data/sub2
/tmp/data/sub2/foo
/tmp/data/sub0
/tmp/data/sub0/foo
/tmp/data/sub1
/tmp/data/sub1/foo
$ tar -zvcf /tmp/_data.tar /tmp/data --exclude='/tmp/data/sub[1-2]'
tar: Removing leading `/' from member names
/tmp/data/
/tmp/data/sub0/
/tmp/data/sub0/foo
$ tar -zvcf /tmp/_data.tar /tmp/data --exclude=/tmp/data/sub[1-2]
tar: Removing leading `/' from member names
/tmp/data/
/tmp/data/sub0/
/tmp/data/sub0/foo
$ echo tar -zvcf /tmp/_data.tar /tmp/data --exclude=/tmp/data/sub[1-2]
tar -zvcf /tmp/_data.tar /tmp/data --exclude=/tmp/data/sub[1-2]
$ tar -zvcf /tmp/_data.tar /tmp/data --exclude /tmp/data/sub[1-2]
tar: Removing leading `/' from member names
/tmp/data/
/tmp/data/sub2/
/tmp/data/sub2/foo
/tmp/data/sub0/
/tmp/data/sub0/foo
/tmp/data/sub2/
tar: Removing leading `/' from hard link targets
/tmp/data/sub2/foo
$ echo tar -zvcf /tmp/_data.tar /tmp/data --exclude /tmp/data/sub[1-2]
tar -zvcf /tmp/_data.tar /tmp/data --exclude /tmp/data/sub1 /tmp/data/sub2
R Perrin
fuente
Gracias por la respuesta muy centrada y clara. Con respecto a su primer punto, estaba tratando de seguir los consejos en este hilo LQ . No estoy seguro de lo que me perdí, pero ahora que leí su segundo punto, es muy posible que sea un problema de ruta absoluta versus relativa. Lo intentaré e informaré. +1 por ahora.
ateiob
Otra cosa que he notado es el --exclude b(espacio en lugar de signo igual) vs --exclude=b. ¿Esto hace alguna diferencia? (no debería en mi humilde opinión)
ateiob
1
El signo igual puede ser esencial para evitar la expansión de los patrones sin comillas. Si en su lugar tiene un espacio, el shell puede expandir un patrón sin comillas en un solo argumento --excluir, y las expansiones restantes se dan como archivos para agregar al archivo tar. Sus ejemplos sobre todo tienen '=': si el script no lo tiene y le faltan comillas simples, entonces ese puede ser el origen de su problema.
R Perrin
OKAY. Probé su ejemplo en mi caja y funciona, incluso con múltiples --exclude=en la misma línea. Entonces, la diferencia debe ser el idiota /*que agregué a cada subdirectorio. Probaré esto esta noche en el guión de producción e informaré. Otro +1.
ateiob
Para mí, la respuesta de @carlo fue el problema específico: el estúpido alquitrán no puede tomar, excluir como la última opción en la línea de comando, obviamente causando muchos dolores de cabeza. Gracias a todos.
moodboom
34

Es posible que su versión de tarrequiera que las --excludeopciones tengan que colocarse al comienzo del tarcomando.

Ver: https://stackoverflow.com/q/984204

tar --exclude='./folder' --exclude='./upload/folder2' \
    -zcvf /backup/filename.tgz .

Ver: http://mandrivausers.org/index.php?/topic/8585-multiple-exclude-in-tar/

tar --exclude=<first> --exclude=<second> -cjf backupfile.bz2 /home/*

Alternativa:

EXCLD='first second third'
tar -X <(for i in ${EXCLD}; do echo $i; done) -cjf backupfile.bz2 /home/*

Otro tarconsejo de comando es de aquí :

tar cvfz myproject.tgz --exclude='path/dir_to_exclude1' \
                       --exclude='path/dir_to_exclude2' myproject
carlo
fuente
Vea mi actualización arriba. La última variación probada (sin comillas, espacio simple) funciona. No tengo ni idea de porqué. +1 para la respuesta bien pensada + enlaces.
ateiob
FYI, bajo debian, si no preciso el filtro, --exclude=mydir/*entonces no funciona (usando tar --exclude=maindir/mydir/* -cjf archive.tar2.bz2 maindir/*).
Olivier Pons
1
@OlivierPons en lugar de "bajo debian", o quizás con él, coloque la versión de tar ( tar --version); se supone que debian se enviará con muchas versiones diferentes de alquitrán a través de los años.
msouth
1
Mi versión (1.29) solo funciona con --excludeantes -czf.
falsePockets
8

Para excluir varios archivos, intente

--exclude=/data/{sub1,sub2,sub3,sub4}

Esto ahorrará algo de código y dolor de cabeza. Esta es una solución global, para todo tipo de programas / opciones. Si también desea incluir el directorio principal en su selección (en este caso, datos), debe incluir una coma final. P.ej:

umount /data/{sub1,sub2,}
tolga9009
fuente
3
Amo los curlies. Me parece que mucha gente no los conoce, incluso con años de experiencia en Unix. mv /very/very/very/very/long/path/to/a/file{,.bak}
msouth
5

Este enlace puede ser útil. http://answers.google.com/answers/threadview/id/739467.html

Dos diferencias inmediatas entre la línea que no funciona y algunos consejos en el enlace:

  1. Todas las exclusiones vienen después del directorio de nivel superior.
  2. No puede haber NINGÚN espacio después del último --exclude.
BigG
fuente
Gracias. La respuesta -MAKme llamó la atención y hasta ahora he podido detectar las siguientes diferencias entre mi línea que no funciona y las siguientes: 1. Todas las exclusiones vienen después del directorio de nivel superior. 2. No puede haber NINGÚN espacio después del último --exclude. Probaré estas ideas e informaré de nuevo. +1 por ahora.
ateiob
@ateiob Si lo resuelves, ¿puedes publicar una respuesta aquí o editar esta? Generalmente preferimos no tener respuestas que sean solo enlaces en otros lugares
Michael Mrozek
@Michael Mrozek Absolutamente. Esto es exactamente lo que escribí en mi comentario. :)
ateiob
3

Una solución alternativa puede ser utilizar una combinación de find ... -pruney tarexcluir los directorios especificados.

En Mac OS X, la --excludeopción de GNU tarparece funcionar como debería.

En el siguiente caso de prueba, los directorios /private/var/log/asly /private/var/log/DiagnosticMessagesse excluirán de un archivo comprimido del /private/var/logdirectorio.

# all successfully tested in Bash shell on Mac OS X (using gnutar and gfind)

# sudo port install findutils  # for gfind from MacPorts

sudo gnutar -czf ~/Desktop/varlog.tar.gz /private/var/log --exclude "/private/var/log/asl" --exclude "/private/var/log/DiagnosticMessages"

sudo gnutar -czf ~/Desktop/varlog.tar.gz  --exclude "/private/var/log/asl" --exclude "/private/var/log/DiagnosticMessages" /private/var/log

set -f # disable file name globbing
sudo gnutar -czf ~/Desktop/varlog.tar.gz  --exclude "/private/var/log/asl" --exclude "/private/var/log/Diagnostic*" /private/var/log

# combining GNU find and tar (on Mac OS X)

sudo gfind /private/var/log -xdev -type d \( -name "asl" -o -name "DiagnosticMessages" \) -prune -o -print0 | 
   sudo gnutar --null --no-recursion -czf ~/Desktop/varlog.tar.gz --files-from -

# exclude even more dirs
sudo gfind /private/var/log -xdev -type d \( -name "asl" -o -name "[Dacfks]*" \) -prune -o -print0 | 
    sudo gnutar --null --no-recursion -czf ~/Desktop/varlog.tar.gz --files-from -


# testing the compressed archive

gnutar -C ~/Desktop -xzf ~/Desktop/varlog.tar.gz

sudo gfind /private/var/log ~/Desktop/private \( -iname DiagnosticMessages -or -iname asl \)

sudo rm -rf ~/Desktop/varlog.tar.gz ~/Desktop/private
jon
fuente
Gracias +1 por la sugerencia. En este punto, todavía estoy tratando de entender por qué una característica bien documentada (y madura) no funciona en mi script, ejecutada todas las noches por cron.
ateiob
3

Quizás pueda probar el comando con otra opción:

--wildcards

Y verifique si está funcionando según lo previsto.

Luis
fuente
Vea mi actualización arriba. La última variación probada (sin comillas, espacio simple) funciona. No tengo ni idea de porqué. +1 por la idea.
ateiob
3

Estoy usando una Mac, y descubrí que las exclusiones no funcionaban a menos que la carpeta de nivel superior sea el último argumento

ejemplo de comando de trabajo:

tar czvf tar.tgz --exclude='Music' dir

FYI:

$: tar --version
bsdtar 2.8.3 - libarchive 2.8.3
frascos99
fuente
Lo mismo ocurre con tar 1.27.1 a través de Ubuntu 14.04.
Greg Bell
3

En mi caso, no excluyó por una razón diferente.

La ruta completa frente a la ruta relativa.

Tanto el excluir como el directorio deben usar el mismo formato de ruta (es decir, la ruta completa o ambas rutas relativas)

Ejemplo:

tar -cvf ctms-db-sync.tar --exclude='/home/mine/tmp/ctms-db-sync/sql' ctms-db-sync

Esto no funcionará porque excluir usa la ruta completa, mientras que el destino usa una ruta relativa

tar -cvf ctms-db-sync.tar --exclude='/home/mine/tmp/ctms-db-sync/sql' /home/mine/tmp/ctms-db-sync

Esto funciona porque ambos usan la ruta completa

tar -cvf ctms-db-sync.tar --exclude='ctms-db-sync/sql' ctms-db-sync

Esto funciona porque ambos usan la ruta relativa

hbt
fuente
1

Notas adicionales a la excelente respuesta de R Perrin :

Suponga que no desea archivar rutas absolutas sino relativas, por ejemplo, 'datos' en lugar de '/ tmp / data'. Para excluir rutas absolutas, sus argumentos tar diferirán en función de la implementación de tar (gnu tar vs bsd tar) que utilice:

$ for i in 0 1 2; do
    for j in 0 1 2; do 
      mkdir -p /tmp/data/sub$i/sub$j
      echo foo > /tmp/data/sub$i/sub$j/foo
    done
  done

$ find /tmp/data/
/tmp/data/
/tmp/data/sub2
/tmp/data/sub2/sub2
/tmp/data/sub2/sub2/foo
/tmp/data/sub2/sub1
/tmp/data/sub2/sub1/foo
/tmp/data/sub2/sub0
/tmp/data/sub2/sub0/foo
/tmp/data/sub1
/tmp/data/sub1/sub2
/tmp/data/sub1/sub2/foo
/tmp/data/sub1/sub1
/tmp/data/sub1/sub1/foo
/tmp/data/sub1/sub0
/tmp/data/sub1/sub0/foo
/tmp/data/sub0
/tmp/data/sub0/sub2
/tmp/data/sub0/sub2/foo
/tmp/data/sub0/sub1
/tmp/data/sub0/sub1/foo
/tmp/data/sub0/sub0
/tmp/data/sub0/sub0/foo

$ cd /tmp/data; tar -zvcf /tmp/_data.tar --exclude './sub[1-2]'
./
./sub0/
./sub0/sub2/
./sub0/sub2/foo
./sub0/sub1/
./sub0/sub1/foo
./sub0/sub0/
./sub0/sub0/foo

# ATTENTION: bsdtar's behaviour differs from traditional tar (without a leading '^')!
$ cd /tmp/data; bsdtar -zvcf /tmp/_data.tar --exclude './sub[1-2]' .
a .
a ./sub0
a ./sub0/sub0
a ./sub0/sub0/foo

# FIX: Use a regex by adding a leading '^' will cause bsdtar to match only parent files and folders.
$ cd /tmp/data; bsdtar -zvcf /tmp/_data.tar --exclude '^./sub[1-2]' .
# ALTERNATIVE: bsdtar -C /tmp/data -zvcf /tmp/_data.tar --exclude '^./sub[1-2]' .
a .
a ./sub0
a ./sub0/sub2
a ./sub0/sub1
a ./sub0/sub0
a ./sub0/sub0/foo
a ./sub0/sub1/foo
a ./sub0/sub2/foo
Jakob
fuente
1

Recién detectado en tar (GNU tar) 1.29

Esta llamada no excluye de los archivos de almacenamiento especificados con --exclude-from:

/bin/tar --files-from ${datafile} --exclude-from ${excludefile} -jcf ${backupfile}

Esta llamada funciona correctamente:

/bin/tar --exclude-from ${excludefile} --files-from ${datafile} -jcf ${backupfile}

¡El orden de los parámetros es importante!

Alejandro
fuente
0

Intenté todo tipo de combinaciones, incluidas algunas de las respuestas enumeradas, pero no pude excluir los archivos enumerados.

Así que, harto de perseguir la respuesta a lo que debía ser un trabajo de cinco minutos, hice lo contrario: creé un archivo de las carpetas que quería incluir.

Hice esto creando un archivo y luego agregándole :

tar -cvpf /path/to/mybackup.tar ./bin
tar rvf /path/to/mybackup.tar ./boot
tar rvf /path/to/mybackup.tar ./etc
tar rvf /path/to/mybackup.tar ./home
tar rvf /path/to/mybackup.tar ./lib
tar rvf /path/to/mybackup.tar ./sbin
tar rvf /path/to/mybackup.tar ./usr
tar rvf /path/to/mybackup.tar ./var

Algunas notas

  • Utilicé las rutas relativas en lugar de absolutas (que también daban problemas) al ejecutar desde la raíz del sistema de archivos.
  • Debe crear un archivo simple tar(y no comprimido .tgz/ tar .tar.gz) puede comprimirlo más tarde usandogzip mybackup.tar
  • Asegúrese de no poner el archivo en ninguna carpeta que esté incluyendo o obtendrá algo de recurrencia (una copia de seguridad parcial también incluida en la copia de seguridad).
  • Tenga en cuenta la diferencia en el primer comando (crear) de los demás (agregar).
  • Puede verificar que los archivos se agreguen en lugar de la copia de seguridad sobrescrita (por ejemplo, después del segundo comando) si está paranoico al usar tar tvf mybackup.tar.
SharpC
fuente