tar --exclude n'exclut pas. Pourquoi?

71

J'ai cette ligne très simple dans un script bash qui s'exécute avec succès (c.-à-d. La production du _data.tarfichier), sauf qu'elle n'exclut pas les sous-répertoires qu'il est dit d'exclure via l' --excludeoption:

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

Au lieu de cela, il produit un _data.tarfichier contenant tout ce qui se trouve sous / data, y compris les fichiers des sous-répertoires que je voulais exclure.

Une idée pourquoi? et comment résoudre ce problème?

Mise à jour J'ai mis en œuvre mes observations sur la base du lien fourni dans la première réponse ci-dessous (répertoire de premier niveau en premier, pas d'espace après la dernière exclusion):

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

Mais ça n'a pas aidé. Tous les sous-répertoires "exclus" sont présents dans le _data.tarfichier résultant .

C'est curieux. Qu'il s'agisse d'un bogue dans le tar actuel (GNU tar 1.23, sur CentOS 6.2, Linux 2.6.32) ou de "l'extrême sensibilité" du tar aux espaces et autres fautes de frappe faciles à oublier, je considère cela comme un bogue. Pour l'instant.

C'est horrible : j'ai essayé les idées suggérées ci-dessous (pas de fin /*) et cela ne fonctionne toujours pas dans le script de production:

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

Je ne vois aucune différence entre ce que j'ai essayé et ce que @Richard Perrin a essayé, à l'exception des guillemets et de 2 espaces au lieu de 1. Je vais essayer (cela doit attendre que le script nocturne soit exécuté comme répertoire à sauvegarder) est énorme) et faire rapport.

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

Je commence à penser que toutes ces tar --excludesensibilités ne sont pas du goudron, mais quelque chose dans mon environnement, mais alors qu'est-ce que cela pourrait être?

Ça a marché! La dernière variante essayée (pas de guillemets simples et d’espaces simples au lieu de doubles espaces entre les --excludes) a fonctionné. Bizarre mais acceptant.

Incroyable! Il s'avère qu'une ancienne version de tar(1.15.1) exclut uniquement si le répertoire de niveau supérieur est le dernier sur la ligne de commande. C’est exactement le contraire de la version 1.23. FYI.

Ateiob
la source

Réponses:

50

Si vous souhaitez exclure un répertoire entier, votre modèle doit correspondre à ce répertoire et non aux fichiers qu'il contient. Utiliser --exclude=/data/sub1au lieu de--exclude='/data/sub1/*'

Faites attention en citant les motifs pour les protéger de l'expansion du shell.

Voir cet exemple, avec des problèmes dans l'invocation finale:

$ 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
la source
Merci pour la réponse très ciblée et claire. En ce qui concerne votre premier point, j'essayais de suivre les astuces de ce fil LQ . Je ne suis pas sûr de ce que j'ai manqué, mais maintenant que je lis votre deuxième point, il peut très bien s'agir d'un problème de chemin absolu ou relatif. Je vais essayer et rendre compte. +1 pour l'instant.
Ateiob
Une autre chose que j'ai remarqué est le --exclude b(espace au lieu de signe égal) contre --exclude=b. Cela fait-il une différence? (ne devrait pas IMHO)
ateiob
1
Le signe égal peut être essentiel pour éviter l’extension en coque de modèles non cités. Si vous avez plutôt un espace, alors un motif non cité peut être développé par le shell en un seul argument --exclude, et les extensions développées donnent comme fichiers à ajouter au fichier tar. Vos exemples ci-dessus ont tous avant '=' - si le script ne le permet pas et s'il manque des guillemets simples, cela peut être la source de votre problème.
R Perrin
D'ACCORD. J'ai testé votre exemple sur ma boîte et cela fonctionne, même avec plusieurs --exclude=sur la même ligne. Donc, la différence doit être l'idiot /*que j'ai ajouté à chaque sous-répertoire. Je vais tester cela ce soir dans le script de production et faire rapport. Un autre +1.
Ateiob
Pour moi, la réponse de @carlo était le problème spécifique - un tar stupide ne peut pas prendre --exclude comme dernière option de la ligne de commande - provoquant évidemment beaucoup de maux de tête. Merci a tous.
moodboom
34

Il se peut que votre version de tarnécessite que les --excludeoptions soient placées au début de la tarcommande.

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

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

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

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

Alternative:

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

Voici un autre tarconseil de commande :

tar cvfz myproject.tgz --exclude='path/dir_to_exclude1' \
                       --exclude='path/dir_to_exclude2' myproject
carlo
la source
Voir ma mise à jour ci-dessus. La dernière variante essayée (sans guillemets, espace unique) fonctionne. Je ne sais pas pourquoi. +1 pour la réponse bien pensée + liens.
ateiob
Pour votre information, sous debian, si je ne précise pas le filtre, comme --exclude=mydir/*alors il ne fonctionne pas (avec tar --exclude=maindir/mydir/* -cjf archive.tar2.bz2 maindir/*).
Olivier Pons
1
@OlivierPons plutôt que "sous debian", ou peut-être avec, mettez la version de tar ( tar --version); Debian sera probablement livré avec de nombreuses versions différentes de goudron au fil des ans.
msouth
1
Ma version (1.29) ne fonctionne qu'avec --excludeavant -czf.
falsePockets
8

Pour exclure plusieurs fichiers, essayez

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

Cela économisera du code et des maux de tête. Ceci est une solution globale, pour tout type de programmes / options. Si vous souhaitez également inclure le répertoire parent dans votre sélection (dans ce cas, les données), vous devez inclure une virgule de fin. Par exemple:

umount /data/{sub1,sub2,}
tolga9009
la source
3
J'aime les curlies. Je trouve que beaucoup de gens ne les connaissent pas, même avec des années d'expérience Unix. mv /very/very/very/very/long/path/to/a/file{,.bak}
msouth
5

Ce lien pourrait être utile. http://answers.google.com/answers/threadview/id/739467.html

Deux différences immédiates entre la ligne qui ne fonctionne pas et quelques astuces dans le lien:

  1. Tous les exclus viennent après le répertoire de niveau supérieur.
  2. Vous ne pouvez avoir AUCUN espace après le dernier --exclude.
BigG
la source
Merci. La réponse a -MAKattiré mon attention et jusqu'à présent, j'ai pu identifier les différences suivantes entre ma ligne qui ne fonctionnait pas et les suivantes: 1. Tous les éléments exclus viennent après le répertoire de niveau supérieur. 2. Ne peut avoir AUCUN espace après le dernier --exclude. Je vais tester ces informations et faire rapport. +1 pour l'instant.
ateiob
@ateiob Si vous le voyez, pouvez-vous poster une réponse ici ou éditer celle-ci? Nous préférons généralement ne pas avoir de réponses qui ne sont que des liens ailleurs
Michael Mrozek
@ Michael Mrozek Absolument. C'est exactement ce que j'ai écrit dans mon commentaire. :)
ateiob
3

Une solution de contournement peut consister à utiliser une combinaison de find ... -pruneet tarà exclure les répertoires spécifiés.

Sur Mac OS X, l’ --excludeoption de GNU tarsemble fonctionner comme il se doit.

Dans le cas de test suivant les répertoires /private/var/log/aslet /private/var/log/DiagnosticMessagesdoivent être exclus d'une archive compressée du /private/var/logrépertoire.

# 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
la source
Merci +1 pour la suggestion. À ce stade, j'essaie encore de comprendre pourquoi une fonctionnalité bien documentée (et mature) ne fonctionne pas dans mon script, exécuté quotidiennement par cron.
ateiob
3

Peut-être que vous pouvez essayer la commande avec une autre option:

--wildcards

Et vérifiez si cela fonctionne comme prévu.

Luis
la source
Voir ma mise à jour ci-dessus. La dernière variante essayée (sans guillemets, espace unique) fonctionne. Je ne sais pas pourquoi. +1 pour l'idée.
Ateiob
3

J'utilise un mac, et trouve que exclut ne fonctionnaient pas, sauf si le dossier de niveau supérieur est le dernier argument

exemple de commande de travail:

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

FYI:

$: tar --version
bsdtar 2.8.3 - libarchive 2.8.3
bocaux99
la source
Il en va de même avec tar 1.27.1 via Ubuntu 14.04.
Greg Bell
3

Dans mon cas, cela n’excluait pas pour une raison différente.

Le chemin complet vs chemin relatif.

L'exclusion et le répertoire doivent utiliser le même format de chemin (c'est-à-dire un chemin complet ou un chemin relatif).

Exemple:

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

Cela ne fonctionnera pas car exclut utilise un chemin complet, tandis que la cible utilise un chemin relatif.

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

Cela fonctionne parce que les deux utilisent le chemin complet

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

Cela fonctionne car les deux utilisent le chemin relatif

hbt
la source
1

Notes complémentaires à l'excellente réponse de R Perrin :

Supposons que vous ne souhaitiez pas archiver des chemins absolus mais relatifs, par exemple, "data" au lieu de "/ tmp / data". Pour exclure les chemins absolus, vos arguments tar diffèrent en fonction de l'implémentation de tar (gnu tar vs. bsd tar) utilisée:

$ 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
la source
1

Tout à l'heure détecté sur tar (GNU tar) 1.29

Cet appel n’exclut pas les fichiers d’archive spécifiés avec --exclude-from:

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

Cet appel fonctionne de manière coordonnée:

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

L'ordre des paramètres est important!

Alexandre
la source
0

J'ai essayé toutes sortes de combinaisons, y compris quelques unes des réponses répertoriées, mais je ne pouvais tout simplement pas l'exclure pour exclure les fichiers listés.

Alors, marre de chercher la réponse à ce qui devait être un travail de cinq minutes, j'ai fait le contraire: j'ai créé une archive des dossiers que je voulais inclure.

Je l'ai fait en créant une archive, puis en y ajoutant :

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

Quelques notes:

  • J'ai utilisé le chemin relatif au lieu du chemin absolu (qui posait également des problèmes) en exécutant à partir de la racine du système de fichiers.
  • Vous devez créer une archive plain tar(et non compressée tar .tgz/ .tar.gz) - vous pouvez la compresser ultérieurement à l'aide degzip mybackup.tar
  • Assurez-vous de ne pas placer l'archive dans le dossier que vous incluez ou vous obtiendrez une certaine récursion (une sauvegarde partielle est également incluse dans la sauvegarde elle-même).
  • Notez la différence entre la première commande (créer) et les autres (ajouter).
  • Vous pouvez vérifier que les fichiers sont ajoutés plutôt que la sauvegarde écrasée (par exemple, après la deuxième commande) si vous êtes paranoïaque en utilisant tar tvf mybackup.tar.
SharpC
la source