Encontrar arquivos e tará-los (com espaços)

110

Tudo bem, problema tão simples aqui. Estou trabalhando em um código de backup simples. Funciona bem, exceto se os arquivos contiverem espaços. É assim que encontro arquivos e os adiciono a um arquivo tar:

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

O problema é quando o arquivo tem um espaço no nome porque o tar pensa que é uma pasta. Basicamente, há uma maneira de adicionar aspas em torno dos resultados de find? Ou uma maneira diferente de consertar isso?

Caleb Kester
fonte
12
A melhor forma de utilização find ... | xargs ...é usar o / -0 parâmetro -print0 em cada: find -print0 ... | xargs -0 .... Isso fará com que os nomes dos arquivos sejam separados por um caractere nulo, o que significa que você pode ter espaços ou novas linhas ou outras coisas estranhas em seus nomes de arquivo e ainda funcionará.
porges
8
Há um problema em usar xargs e tar desta forma quando você tem um grande número de arquivos, xargs irá invocar tar -c repetidamente, e isso irá manter sobrescrevendo seu arquivo, e o resultado é que você não terá todos os arquivos que espera . Veja esta explicação mais detalhada e minha resposta abaixo.
Steve Kehlet

Respostas:

217

Usa isto:

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

Será:

  • lidar com arquivos com espaços, novas linhas, travessões e outras coisas engraçadas
  • lidar com um número ilimitado de arquivos
  • não sobrescreverá repetidamente seu backup.tar.gz como usar tar -ccom xargsfará quando você tiver um grande número de arquivos

Veja também:

Steve Kehlet
fonte
1
como você faria isso se quisesse canalizar sua descoberta através do sed algumas vezes primeiro? por exemplo, encontrar. -print0 | sed / backups / d | tar ....
Brad Parks
8
Observe que se houver várias condições, você precisará adicionar parênteses. Caso contrário, o -print0aplica-se apenas à última expressão. Exfind . \( -type f -o -name '*.c' \) -print0 | ...
nimrodm
1
Para se divertir, aqui está uma versão do Windows 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 pode explicar o que é a opção '-' no final do comando tar. Não consigo encontrar na página de manual do GNU tar.
shaffooo
Claro, é um parâmetro para -T, e significa ler os nomes dos arquivos da entrada padrão: Se você fornecer um único traço como um nome de arquivo para `--files-from ', (ou seja, você especifica --files-from = - ou -T -), então os nomes dos arquivos são lidos a partir da entrada padrão
Steve Kehlet
14

Pode haver outra maneira de conseguir o que você deseja. Basicamente,

  1. Use o comando find para gerar o caminho para os arquivos que você está procurando. Redirecione stdout para um nome de arquivo de sua escolha.
  2. Em seguida, tar com a opção -T que permite obter uma lista de locais de arquivos (aquele que você acabou de criar com find!)

    find . -name "*.whatever" > yourListOfFiles
    tar -cvf yourfile.tar -T yourListOfFiles
    
propenso a erros
fonte
Há uma resposta aqui sobre como lidar com nomes de arquivo com novas linhas: superuser.com/a/513319/151261
tommy.carstensen
8

Tente executar:

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

Por que não:

tar czvf backup.tar.gz *

Claro, é inteligente usar find e depois xargs, mas você está fazendo isso da maneira mais difícil.

Atualização: Porges comentou com uma opção de localização que eu acho que é uma resposta melhor do que a minha, ou a outra: find -print0 ... | xargs -0 ....

Warren P
fonte
Meu código completo fará backup apenas dos itens modificados no dia anterior. Como é um backup diário, não quero ter informações repetidas para salvar no tamanho do arquivo (também tenho um backup completo a cada 15 dias).
Caleb Kester
Para tornar essa pergunta SO melhor, eu faria a pergunta sobre "usar find, xargs e tar juntos de maneira confiável". Seu título e sua pergunta realmente não especificam que você precisa de find e xargs, e mesmo assim precisa.
Warren P
xargs ... tar c ...irá sobrescrever o primeiro arquivo criado se a lista de arquivos for muito longa e xargsserá executado tarpela segunda vez! Para evitar a substituição, você pode usar, xargs -xmas o arquivo pode estar incompleto. A alternativa pode ser primeiro tar c ...e depois, possivelmente, repetidamente tar r .... (minha contribuição para a confiabilidade :)
pabouk
3

Se você tiver vários arquivos ou diretórios e quiser compactá-los em um *.gzarquivo independente, poderá fazer isso. Opcional-type f -atime

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

Isso vai comprimir

httpd-log01.txt
httpd-log02.txt

para

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

Por que não experimentar algo assim: tar cvf scala.tar `find src -name *.scala`

Frank Eggink
fonte
2

Outra solução como vista aqui :

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

Gostaria de adicionar um comentário ao post de @Steve Kehlet, mas precisa de 50 representantes (RIP).

Para qualquer pessoa que encontrou esta postagem por meio de várias pesquisas no Google, encontrei uma maneira de não apenas encontrar arquivos específicos em um intervalo de tempo, mas também NÃO incluir os caminhos relativos OU espaços em branco que causariam erros de tarring. (MUITO OBRIGADO, STEVE.)

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

  2. -name "*.pdf" procure pdfs (ou qualquer tipo de arquivo)

  3. -type f o tipo a procurar é um arquivo

  4. -mtime 0 procure os arquivos criados nas últimas 24 horas

  5. -printf "%f\0"Regular -print0OU -printf "%f"NÃO funcionou para mim. Das páginas de manual:

Esta citação é realizada da mesma maneira que para GNU ls. Este não é o mesmo mecanismo de cotação usado para -ls e -fls. Se você for capaz de decidir qual formato usar para a saída de find, normalmente é melhor usar '\ 0' como terminador do que usar nova linha, pois os nomes dos arquivos podem conter espaços em branco e caracteres de nova linha.

  1. -czvf criar arquivo, filtrar o arquivo através do gzip, listar detalhadamente os arquivos processados, nome do arquivo

Editar 14-08-2019: gostaria de acrescentar que também fui capaz de usar essencialmente o mesmo comando em meu comentário, apenas usando o próprio tar:

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

Necessário --ignore-failed-readno caso de não haver novos PDFs para hoje.

user3472383
fonte
1

A melhor solução parece ser criar uma lista de arquivos e, em seguida, arquivá-los, porque você pode usar outras fontes e fazer outra coisa com a lista.

Por exemplo, isso permite usar a lista para calcular o tamanho dos arquivos que estão sendo arquivados:

#!/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
fonte
Um forro para isso?
Robino,