Ist es möglich, eine .tar.gz-Datei direkt aus stdin zu erstellen? Oder ich muss bereits komprimierte Dateien zusammen tarieren

7

Ich werde Ihnen genau sagen, was ich brauche, um die kryptische Frage im Titel zu klären. Ich mache derzeit geplante MySQL-Backups aller meiner Datenbanken mit etwas wie:

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

Dies ist in Ordnung, aber ich bin bereit, für jede einzelne Datenbank eine separate Datei zu erstellen, da dies das Betrachten von Speicherauszügen oder das Wiederherstellen einer einzelnen Datenbank erleichtert:

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

Ich möchte alle Speicherauszüge für jedes einzelne Backup in einer einzigen .tarDatei speichern, dh mysql-backup.tar.gzmit allen darin enthaltenen Speicherauszugsdatenbanken . Ich weiß, dass ich .sqlDateien einfach unkomprimiert lassen kann 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 wird tatsächlich mysqldumpin weitergeleitet gzip, sodass keine große Datei erstellt wird.

2) Gibt es eine ähnliche Art und Weise, wie ich .tar.gzaus stdin erstellen kann ?

3) tar -c *.sql.gzEntspricht tar -cz *.sql?

Lorenzo-s
quelle
3
@jhilmer Bei verknüpften Fragen geht es darum, Dateinamen von stdin abzurufen, nicht von tatsächlichen Daten.
Lorenzo-s
1
Ist tar -c *.sql.gzäquivalent zu tar -cz *.sql? - Nein, letzteres ist etwas effizienter, aber das macht für viele kleine Dateien einen größeren Unterschied als für einige wenige große Dateien.
lcd047

Antworten:

5

Ich habe Python zusammengeschustert, um zu tun, was du willst. Es verwendet die Tarfile- Bibliothek von Python , um stdin an eine tar-Datei anzuhängen, und sucht dann einfach im tar zurück, 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 tarappendPython-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)
meuh
quelle
4

Nicht einfach. tarZeichnet nicht nur Dateiinhalte auf, sondern auch Dateimetadaten (Name, Zeitstempel, Berechtigungen, Eigentümer usw.). Diese Informationen müssen von irgendwoher kommen und werden nicht in einer Pipe vorhanden sein.

Sie können Ihre Datenbank-Dumps in eine Datei gzipen (wahrscheinlich nach der betreffenden Datenbank benannt), die Datei an ein Tar-Archiv anhängen und die Datei dann löschen, bevor Sie mit der nächsten Datenbank fortfahren. Dies würde zu einer .gz.tar-Datei führen, die ungewöhnlich, aber keineswegs problematisch ist und wahrscheinlich nicht wesentlich mehr Festplatte verwendet als das Zippen eines gesamten Datenbankspeicherauszugs (sie wird etwas weniger effizient komprimiert, da dies nicht möglich ist über Datenbankgrenzen hinweg teilen).

Calle Dybedahl
quelle
1

Nein, und ich vermisse diese Funktion so sehr: meine Frage zu Ask Ubuntu .

Wenn es sich bei der zu archivierenden Datei um eine Rohdatei handelt, der keine Dateisystem-Metadaten zugeordnet tarsind, ist weder ein Dateiname noch ein Pfad erforderlich, um den internen Verzeichnis- / Dateibaum zu erstellen (gelinde gesagt).

Ich denke, dass etwas in Perl getan werden kann, das eine Bibliothek hat, die sich der Komprimierung / Dekomprimierung / Archivierung von Dateien widmet: Sehen Sie, ob Sie diese Antwort optimal nutzen können: eine verwandte Antwort auf Ask Ubuntu .

kos
quelle
0

Sie könnten den verspäteten Teer-Postprozessor verwenden.

Sie können jedoch die Verwendung tar anderer Methoden zur Archivierung Ihrer Daten in Frage stellen und in Betracht ziehen. Betrachten Sie insbesondere rsync und afio

Beachten Sie, dass mysqldump die --export-allOption verstanden wird (siehe hier ). Sie können das in ein Skript einfügen, das die Grenzen usw. versteht.

Basile Starynkevitch
quelle
0

Hier ist, was ich getan habe, eine tmp-Datei zu erstellen (und sie anschließend zu löschen)

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