É possível fazer um arquivo .tar.gz diretamente do stdin? Ou, eu preciso tar juntos já arquivos gzipados

7

Vou dizer exatamente o que preciso para esclarecer a questão enigmática do título. Atualmente estou fazendo backups agendados do MySQL de todos os meus bancos de dados com algo como:

mysqldump ... | gzip -c > mysql-backup.gz

Está tudo bem, mas estou disposto a criar um arquivo separado para cada banco de dados, pois isso facilitará a visualização dos dados descartados ou a restauração de um único banco de dados:

for db in $dbs; do mysqldump ... $db | gzip -c > mysql-backup-$db.gz; done

Gostaria de armazenar todos os despejos para cada backup em um único arquivo .tar , ou seja, mysql-backup.tar.gz com todos os bancos de dados despejados. Eu sei que posso simplesmente deixar .sql arquivos descomprimidos e, em seguida, tar -cz *.sql , mas 1) Estou procurando uma maneira que não precise armazenar temporariamente grandes arquivos . No meu script atual, de fato, mysqldump é canalizado para gzip , portanto nenhum arquivo grande é criado.

2) Existe uma maneira similar na qual eu posso criar .tar.gz de stdin ?

3) É tar -c *.sql.gz eqivalente para tar -cz *.sql ?

    
por lorenzo-s 08.07.2015 / 09:32

5 respostas

5

Eu montei alguns python para fazer o que você quer. Ele usa a biblioteca python's tarfile para anexar stdin a um arquivo tar, e então simplesmente procura de volta no tar para reescrever o cabeçalho com o tamanho correto em eof. O uso seria:

rm -f mytar
for db in $dbs
do mysqldump ... $db | gzip -c |
   tarappend -t mytar -f mysql-backup-$db.gz
done
tar tvf mytar

Este é o script tarappend python:

#!/usr/bin/python
# concat stdin to end of tar file, with given name. meuh on stackexchange
# $Id: tarappend,v 1.3 2015/07/08 11:31:18 meuh $

import sys, os, tarfile, time, copy
from optparse import OptionParser
try:
    import grp, pwd
except ImportError:
    grp = pwd = None

usage = """%prog: ... | %prog -t tarfile -f filename
Appends stdin to tarfile under the given arbitrary filename.
tarfile is created if it does not exist.\
"""

def doargs():
    parser = OptionParser(usage=usage)
    parser.add_option("-f", "--filename", help="filename to use")
    parser.add_option("-t", "--tarfile", help="existing tar archive")
    (options, args) = parser.parse_args()
    if options.filename is None or options.tarfile is None:
        parser.error("need filename and tarfile")
    if len(args):
        parser.error("unknown args: "+" ".join(args))
    return options

def copygetlen(fsrc, fdst):
    """copy data from file-like object fsrc to file-like object fdst. return len"""
    totlen = 0
    while 1:
        buf = fsrc.read(16*1024)
        if not buf:
            return totlen
        fdst.write(buf)
        totlen += len(buf)

class TarFileStdin(tarfile.TarFile):
    def addstdin(self, tarinfo, fileobj):
        """Add stdin to archive. based on addfile() """
        self._check("aw")
        tarinfo = copy.copy(tarinfo)
        buf = tarinfo.tobuf(self.format, self.encoding, self.errors)
        bufoffset = self.offset
        self.fileobj.write(buf)
        self.offset += len(buf)

        tarinfo.size = copygetlen(fileobj, self.fileobj)
        blocks, remainder = divmod(tarinfo.size, tarfile.BLOCKSIZE)
        if remainder > 0:
            self.fileobj.write(tarfile.NUL * (tarfile.BLOCKSIZE - remainder))
            blocks += 1
        self.offset += blocks * tarfile.BLOCKSIZE
        # rewrite header with correct size
        buf = tarinfo.tobuf(self.format, self.encoding, self.errors)
        self.fileobj.seek(bufoffset)
        self.fileobj.write(buf)
        self.fileobj.seek(self.offset)
        self.members.append(tarinfo)

class TarInfoStdin(tarfile.TarInfo):
    def __init__(self, name):
        if len(name)>100:
            raise ValueError(name+": filename too long")
        if name.endswith("/"):
            raise ValueError(name+": is a directory name")
        tarfile.TarInfo.__init__(self, name)
        self.size = 99
        self.uid = os.getuid()
        self.gid = os.getgid()
        self.mtime = time.time()
        if pwd:
            self.uname = pwd.getpwuid(self.uid)[0]
            self.gname = grp.getgrgid(self.gid)[0]

def run(tarfilename, newfilename):
    tar = TarFileStdin.open(tarfilename, 'a')
    tarinfo = TarInfoStdin(newfilename)
    tar.addstdin(tarinfo, sys.stdin)
    tar.close()

if __name__ == '__main__':
    options = doargs()
    run(options.tarfile, options.filename)
    
por 08.07.2015 / 13:38
4

Não é fácil. tar registra não apenas o conteúdo do arquivo, mas também os metadados do arquivo (nome, timestamps, permissões, proprietário e outros). Essa informação tem que vir de algum lugar, e não estará lá em um tubo.

Você poderia gzip seus dumps de banco de dados para um arquivo (provavelmente nomeado para o banco de dados em questão), anexar o arquivo a um arquivo tar e, em seguida, excluir o arquivo antes de prosseguir para o próximo banco de dados. Isso resultaria em um arquivo .gz.tar, que é incomum, mas de forma alguma um problema, e provavelmente não usa significativamente mais disco do que gzipar um dump de banco de dados inteiro (ele será um pouco menos eficientemente comprimido, já que não pode compartilhar entre as bordas do banco de dados).

    
por 08.07.2015 / 09:44
1

Não, e eu sinto muita falta desse recurso: my pergunta sobre Ask Ubuntu .

Se o arquivo a ser arquivado for um arquivo bruto sem metadados do sistema de arquivos associados, tar não possui nem um nome de arquivo nem um caminho necessário para construir a árvore de diretórios / arquivos internos (para dizer o mínimo).

Eu acho que algo pode ser feito em Perl, que tem alguma biblioteca dedicada a compressão / descompactação / arquivamento de arquivos: veja se você pode tirar o máximo proveito desta resposta: uma resposta relacionada no Ask Ubuntu .

    
por 08.07.2015 / 09:55
0

Você pode considerar o uso do atraso do pós-processador de tar.

No entanto, você pode questionar o uso de tar e considerar outras maneiras de arquivar suas coisas. Em particular, considere rsync e afio

Observe que mysqldump compreende a opção --export-all (consulte this ). Você pode canalizar isso em algum script entendendo os limites, etc ...

    
por 08.07.2015 / 09:44
0

aqui está o que eu fiz, criando um arquivo tmp (e excluindo-o depois)

temp=$(mktemp)
trap "rm $temp" EXIT
echo 'blabla' >$temp
tar czf - $temp
    
por 09.12.2018 / 22:40