Ich werde Ihnen genau sagen, was ich brauche, um die kryptische Frage im Titel zu klären. Ich erstelle derzeit geplante MySQL-Sicherungen aller meiner Datenbanken mit etwas wie:
mysqldump ... | gzip -c > mysql-backup.gz
Das ist in Ordnung, aber ich bin bereit, eine separate Datei für jede einzelne Datenbank zu erstellen, da dies es einfacher macht, einen Blick auf gesicherte Daten zu werfen oder eine einzelne Datenbank wiederherzustellen:
for db in $dbs; do mysqldump ... $db | gzip -c > mysql-backup-$db.gz; done
Ich möchte alle Dumps für jedes einzelne Backup in einer einzigen .tar
speichern Datei, also mysql-backup.tar.gz
mit all den gedumpten Datenbanken darin. Ich weiß, dass ich .sql
einfach verlassen kann Dateien unkomprimiert und dann tar -cz *.sql
, aber 1) Ich suche nach einer Möglichkeit, große Dateien nicht vorübergehend zu speichern . In meinem aktuellen Skript nämlich mysqldump
wird in gzip
geleitet , sodass keine große Datei erstellt wird.
2) Gibt es eine ähnliche Möglichkeit, wie ich .tar.gz
erstellen kann von stdin ?
3) Ist tar -c *.sql.gz
entspricht tar -cz *.sql
?
Akzeptierte Antwort:
Ich habe Python zusammengeschustert, um zu tun, was Sie wollen. Es verwendet die Tarfile von Python Bibliothek, um stdin an eine tar-Datei anzuhängen, und sucht dann einfach im tar nach, um den Header mit der richtigen Größe bei eof neu zu schreiben. Die Verwendung wäre:
rm -f mytar
for db in $dbs
do mysqldump ... $db | gzip -c |
tarappend -t mytar -f mysql-backup-$db.gz
done
tar tvf mytar
Hier ist das tarappend
Python-Skript:
#!/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)