是否可以直接从标准输入创建 .tar.gz 文件?或者,我需要将已经 gzip 压缩的文件打包在一起

是否可以直接从标准输入创建 .tar.gz 文件?或者,我需要将已经 gzip 压缩的文件打包在一起

我将准确地告诉你我需要什么来澄清标题中的神秘问题。我目前正在对所有数据库进行计划的 MySQL 备份,如下所示:

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

没关系,但我愿意为每个数据库创建一个单独的文件,因为这将使查看转储的数据或恢复单个数据库变得更容易:

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

我想将每个备份的所有转储存储在一个.tar文件中,即mysql-backup.tar.gz包含所有转储的数据库。我知道我可以简单地保留.sql文件未压缩,然后tar -cz *.sql,但是1)我正在寻找一种不需要临时存储大文件的方法。事实上,在我当前的脚本中,mysqldump通过管道传输到gzip,因此不会创建大文件。

2)是否有类似的方式可以.tar.gz从标准输入创建

3)tar -c *.sql.gz等于tar -cz *.sql?

答案1

我拼凑了一些 python 来做你想做的事。它用python 的 tar 文件库将 stdin 附加到 tar 文件,然后简单地在 tar 中查找以在 eof 处重写具有正确大小的标头。用法是:

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

这是tarappendpython 脚本:

#!/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)

答案2

不容易。tar不仅记录文件内容,还记录文件元数据(名称、时间戳、权限、所有者等)。这些信息必须来自某个地方,而且不会存在于管道中。

您可以将数据库转储 gzip 到一个文件(可能以相关数据库命名),将该文件附加到 tar 存档中,然后在继续处理下一个数据库之前删除该文件。这会产生一个 .gz.tar 文件,这是不寻常的,但绝不是问题,而且可能不会比 gzip 压缩整个数据库转储使用更多的磁盘(它的压缩效率会稍低一些,因为它不能跨数据库边界共享)。

答案3

不,我非常想念这个功能:我在 Ask Ubuntu 上的问题

如果要归档的文件是一个原始文件,没有与之关联的文件系统元数据,tar则既没有文件名,也没有构建内部目录/文件树所需的路径(至少可以这么说)。

我认为可以在 Perl 中做一些事情,它有一些专门用于文件压缩/解压缩/归档的库:看看您是否可以从这个答案中获得最大收益:Ask Ubuntu 上的相关答案

答案4

这就是我所做的,创建一个 tmp 文件(然后删除它)

temp=$(mktemp)
trap "rm $temp" EXIT
echo 'blabla' >$temp
tar czf - $temp

相关内容