tar --exclude não exclui. Por quê?

71

Eu tenho essa linha muito simples em um script bash que é executado com sucesso (ou seja, produzindo o _data.tararquivo), exceto que ele não exclui os subdiretórios, é dito que ele exclui por meio da --excludeopção:

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

Em vez disso, produz um _data.tararquivo que contém tudo em / data, incluindo os arquivos nos subdiretórios que eu queria excluir.

Alguma idéia do porquê? e como consertar isso?

Atualização Eu implementei minhas observações com base no link fornecido na primeira resposta abaixo (dir de nível superior primeiro, sem espaço em branco após a última exclusão):

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

Mas isso não ajudou. Todos os subdiretórios "excluídos" estão presentes no _data.tararquivo resultante .

Isso é intrigante. Se isso é um bug no tar atual (GNU tar 1.23, no CentOS 6.2, Linux 2.6.32) ou "extrema sensibilidade" do tar para espaços em branco e outros erros fáceis de perder, considero isso um bug. Para agora.

Isso é horrível : tentei o insight sugerido abaixo (sem rastro /*) e ele ainda não funciona no script de produção:

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

Não vejo diferença entre o que tentei e o que Richard Perrin tentou, exceto as aspas e 2 espaços em vez de 1. Vou tentar isso (devo esperar que o script noturno seja executado como o diretório a ser copiado é enorme) e relata.

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

Estou começando a pensar que todas essas tar --excludesensibilidades não são alcatrão, mas algo no meu ambiente, mas então o que poderia ser isso?

Funcionou! A última variação tentada (sem aspas simples e espaço único em vez de espaço duplo entre os --exclude) testou o funcionamento. Estranho, mas aceitando.

Inacreditável! Acontece que uma versão mais antiga tar(1.15.1) excluiria apenas se o dir de nível superior fosse o último na linha de comando. Esse é exatamente o oposto de como a versão 1.23 exige. PARA SUA INFORMAÇÃO.

ateiob
fonte

Respostas:

50

Se você deseja excluir um diretório inteiro, seu padrão deve corresponder a esse diretório, não aos arquivos nele. Use em --exclude=/data/sub1vez de--exclude='/data/sub1/*'

Tenha cuidado ao citar os padrões para protegê-los da expansão do shell.

Veja este exemplo, com problemas na chamada 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
fonte
Obrigado pela resposta muito focada e clara. Em relação ao seu primeiro ponto, eu estava tentando seguir as dicas neste tópico do LQ . Não tenho certeza do que perdi, mas agora que li o seu segundo ponto, pode muito bem ser um problema de caminho absoluto versus relativo. Vou tentar isso e relatar de volta. +1 por enquanto.
Ateob
Outra coisa que notei é o --exclude b(espaço em vez de sinal de igual) vs. --exclude=b. isso faz alguma diferença? (não deveria IMHO)
ateiob
11
O sinal de igual pode ser essencial para evitar a expansão do shell de padrões não citados. Se você tiver um espaço, um padrão sem aspas poderá ser expandido pelo shell em um único argumento --exclude, e as expansões restantes fornecerão como arquivos para adicionar ao arquivo tar. Seus exemplos, acima de tudo, têm '=' - se o script não existir e estiver faltando aspas simples, isso pode ser a fonte do seu problema.
R Perrin
ESTÁ BEM. Testei o seu exemplo na minha caixa e ele funciona, mesmo com vários --exclude=na mesma linha. Portanto, a diferença deve ser o idiota /*que eu anexei a cada subdiretório. Vou testar isso hoje à noite no script de produção e relatar de volta. Outro +1.
ateiob 29/02
Para mim, a resposta do @carlo foi o problema específico - o tar estúpido não pode ser considerado - como a última opção na linha de comando - obviamente causando muitas dores de cabeça. Obrigado a todos.
Moodboom 29/11/16
34

Pode ser que sua versão tarexija que as --excludeopções sejam colocadas no início do tarcomando.

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

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

Veja: 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/*

Ainda outra tardica de comando é daqui :

tar cvfz myproject.tgz --exclude='path/dir_to_exclude1' \
                       --exclude='path/dir_to_exclude2' myproject
carlo
fonte
Veja minha atualização acima. A última variação tentada (sem aspas, espaço único) funciona. Eu não tenho ideia do porquê. +1 para a resposta bem pensada + links.
ateiob
Para sua informação, no debian, se eu não precisar o filtro, por exemplo --exclude=mydir/*, ele não funciona (usando tar --exclude=maindir/mydir/* -cjf archive.tar2.bz2 maindir/*).
Olivier Pons
11
@OlivierPons em vez de "under debian", ou talvez com ele, coloca a versão do tar ( tar --version); O debian provavelmente será enviado com muitas versões diferentes de tar ao longo dos anos.
9789 msouth
11
Minha versão (1.29) só funciona com --excludeantes -czf.
falsePockets
8

Para excluir vários arquivos, tente

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

Isso economizará algum código e dor de cabeça. Esta é uma solução global para todos os tipos de programas / opções. Se você também deseja incluir o diretório pai em sua seleção (nesse caso, dados), é necessário incluir uma vírgula à direita. Por exemplo:

umount /data/{sub1,sub2,}
tolga9009
fonte
3
Eu amo cachos. Eu acho que muitas pessoas não sabem sobre eles, mesmo com anos de experiência em unix. mv /very/very/very/very/long/path/to/a/file{,.bak}
msouth
5

Esse link pode ser útil. http://answers.google.com/answers/threadview/id/739467.html

Duas diferenças imediatas entre a linha que não está funcionando e algumas dicas no link:

  1. Todas as exclusões vêm após o diretório de nível superior.
  2. Não pode ter QUALQUER espaço após o último --exclude.
BigG
fonte
Obrigado. A resposta -MAKchamou minha atenção e até agora consegui identificar as seguintes diferenças entre minha linha que não está funcionando e a seguinte: 1. Todas as exclusões vêm após o diretório de nível superior. 2. Não pode ter nenhum espaço após o último --exclude. Vou testar essas idéias e relatar de volta. +1 por enquanto.
ateiob 27/02
@ateiob Se você descobrir, pode postar uma resposta aqui ou editar esta? Geralmente, preferimos não ter respostas que são apenas links em outros lugares
Michael Mrozek
@Michael Mrozek Absolutamente. Foi exatamente isso que escrevi no meu comentário. :)
ateiob 27/02
3

Uma solução alternativa pode ser usar uma combinação find ... -prunee tarexcluir os diretórios especificados.

No Mac OS X, a --excludeopção GNU tarparece funcionar como deveria.

No caso de teste a seguir, os diretórios /private/var/log/asle /private/var/log/DiagnosticMessagesdevem ser excluídos de um arquivo compactado do /private/var/logdiretório.

# 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
fonte
Obrigado +1 pela sugestão. Neste ponto, ainda estou tentando entender por que um recurso bem documentado (e maduro) não está funcionando no meu script, executado todas as noites pelo cron.
ateiob
3

Talvez você possa tentar o comando com outra opção:

--wildcards

E verifique se está funcionando conforme o esperado.

Luis
fonte
Veja minha atualização acima. A última variação tentada (sem aspas, espaço único) funciona. Eu não tenho ideia do porquê. +1 para a ideia.
ateiob
3

Estou usando um mac e descobri que as exclusões não estavam funcionando, a menos que a pasta de nível superior seja o último argumento

exemplo de comando de trabalho:

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

PARA SUA INFORMAÇÃO:

$: tar --version
bsdtar 2.8.3 - libarchive 2.8.3
jars99
fonte
O mesmo acontece com o tar 1.27.1 via Ubuntu 14.04.
Greg Bell
3

No meu caso, não foi excluído por um motivo diferente.

O caminho completo vs o caminho relativo.

O diretório de exclusão e o diretório devem usar o mesmo formato de caminho (ou seja, o caminho completo ou os caminhos relativos)

Exemplo:

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

Isso não funcionará porque exclude usa o caminho completo, enquanto o destino usa um caminho relativo

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

Isso funciona porque ambos usam o caminho completo

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

Isso funciona porque ambos usam o caminho relativo

hbt
fonte
1

Notas adicionais à excelente resposta de R Perrin :

Suponha que você não queira arquivar caminhos absolutos, mas relativos, por exemplo, 'dados' em vez de '/ tmp / data'. Para excluir caminhos absolutos, seus argumentos de tar diferem com base na implementação de tar (gnu tar x bsd tar) que você usa:

$ 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
fonte
1

Detectado agora no alcatrão (tar GNU) 1,29

Essa chamada não exclui dos arquivos especificados com --exclude-from:

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

Essa chamada funciona de forma coor correto:

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

A ordem dos parâmetros é importante!

Alexander
fonte
0

Tentei todos os tipos de combinações, incluindo algumas das respostas listadas e simplesmente não consegui excluir os arquivos listados.

Então, farto de procurar a resposta para o que deveria ser um trabalho de cinco minutos, fiz o oposto: criei um arquivo das pastas que queria incluir.

Eu fiz isso criando um arquivo e adicionando a ele :

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

Algumas notas:

  • Usei os caminhos relativo, em vez de absoluto (que também estavam causando problemas), executando a partir da raiz do sistema de arquivos.
  • Você deve criar um arquivo simples tar(e não compactado tar .tgz/ .tar.gz) - você pode compactá-lo posteriormente usandogzip mybackup.tar
  • Certifique-se de não colocar o arquivo morto em nenhuma pasta que esteja incluindo ou obterá alguma recursão (um backup parcial também incluído no próprio backup).
  • Observe a diferença no primeiro comando (criar) dos outros (adicionar).
  • Você pode verificar se os arquivos estão sendo adicionados e não o backup substituído (por exemplo, após o segundo comando) se você estiver paranóico usando tar tvf mybackup.tar.
SharpC
fonte