Wie kann ich den Inhalt einer Tar-Datei filtern und eine andere Tar-Datei in der Pipe erzeugen?

13

Stellen Sie sich eine einzelne TAR-Datei von einem externen System vor, die einige Verzeichnisse mit verschiedenen Attributen enthält, die ich beibehalten möchte, z. B. Berechtigungen, MTimes usw. Wie kann ich eine Teilmenge dieser Dateien als regulärer Benutzer (nicht als Root) verwenden?

Suchen Sie nach etwas wie:

tar -f some.tar.gz --subset subdir/ | ssh remote@system tar xvz

Es ist auch wichtig, dass die Hauptattribute (Besitz, Gruppe, Modus, Mtime) in diesem Tar-Archiv erhalten bleiben. Was ist mit anderen Attributen in einer TAR-Datei wie erweiterten Header-Schlüsselwörtern ?

Bonuspunkte für eine Lösung, die die Verwendung eines temporären Verzeichnisses für den Fall vermeidet, dass dieses Unterverzeichnis sehr große Dateien enthält.

Lekensteyn
quelle

Antworten:

14

bsdtar (basierend auf libarchive) kann tar (und einige andere Archive) von stdin nach stdout filtern. Es kann zum Beispiel nur Dateinamen durchlaufen, die mit einem Muster übereinstimmen , und es kann s/old/new/umbenannt werden. Es ist bereits für die meisten Distributionen verpackt, zum Beispiel bsdtarin Ubuntu.

sudo apt-get install bsdtar   # or aptitude, if you have it.

# example from the man page:
bsdtar -c -f new.tar --include='*foo*' @old.tgz
#create new.tar containing only entries from old.tgz containing the string ‘foo’
bsdtar -czf - --include='*foo*' @-  # filter stdin to stdout, with gzip compression of output.

Beachten Sie, dass es eine große Auswahl an Komprimierungsformaten für die Eingabe / Ausgabe gibt, sodass Sie gunzip / lz4 nicht manuell durchlaufen müssen. Sie können -für stdin mit der @tarfileSyntax und / oder -für stdout wie gewohnt verwenden.


Meine Suche ergab auch, dass dieses Tool zum Ändern von Streaming-Tar die gewünschten Archivänderungen mithilfe von Javascript definieren soll. (Ich denke, das Ganze ist in js geschrieben).

https://github.com/mafintosh/tar-stream

Peter Cordes
quelle
1
Ausgezeichnet, wusste nicht, dass dieser @original.tarAnsatz mit bsdtar möglich war. Scheint auch mit erweiterten Attributen und Komprimierung zu arbeiten </var/cache/pacman/pkg/libuv-1.7.0-1-x86_64.pkg.tar.xz bsdtar -czf - --include='usr/share/*' @- | tar tvz(und aus irgendeinem Grund erzeugt eine leere Auswahl eine Reihe von Null-Bytes, aber das ist für mich kein großes Problem).
Lekensteyn
1
Laut meinen Tests s/old/new/ funktioniert dies nicht für Dateien, die mit @ old.tgz aus alten Archiven stammen, sondern nur für echte Dateien, die direkt aus dem Dateisystem archiviert werden. Es ist wirklich eine Schande, da es der nützlichste Anwendungsfall für mich wäre.
Bart
4

Am einfachsten wäre es, das gesamte Archiv zu kopieren. Ich nehme an, du willst das nicht, weil es zu groß ist.

Die üblichen Befehlszeilentools ( tar, pax) unterstützen das Kopieren von Mitgliedern eines Archivs in ein anderes Archiv nicht.

Wenn Sie die Inhaberschaft nicht beibehalten müssen, würde ich die Verwendung von FUSE- Dateisystemen vorschlagen . Mit archivemount können Sie ein Archiv als Dateisystem bereitstellen . Führen Sie dies für das Quellarchiv aus und führen Sie tar auf dem bereitgestellten Dateisystem aus.

archivemount some.tar.gz mnt
cd mnt
tar -cz subdir | ssh example.com tar -xz
fusermount -u mnt

Alternativ können Sie AVFS verwenden :

mountavfs
cd ~/.avfs$PWD/some.tar.gz\#
tar -cz subdir | ssh example.com tar -xz

Alternativ können Sie tardas ursprüngliche Archiv ausführen und über SSHFS auf den Remotecomputer extrahieren .

sshfs example.com: mnt
cd mnt
tar -xf /path/to/some.tar.gz subdir
fusermount -u mnt

Alle diese Methoden sind jedoch umständlich, wenn Sie den Besitz behalten müssen. Bei allen handelt es sich um das Extrahieren in eine Datei auf dem lokalen Computer. Daher muss der Besitz dieser Datei der beabsichtigte Remote- Besitz sein. Dies erfordert die Ausführung als root und führt möglicherweise nicht zum gewünschten Ergebnis, wenn sich die Dateien im Besitz von Konten befinden, deren Namen oder IDs sich zwischen dem lokalen Computer und dem Remote-Host unterscheiden.

Die tarfileBibliothek von Python bietet eine relativ einfache Möglichkeit, Tar-Mitglieder zu bearbeiten, sodass Sie sie von einer Tar-Datei in eine andere mischen können. Es unterstützt POSIX-Standardformate (ustar, pax) sowie einige GNU-Erweiterungen. Hier ist ein ungetestetes Python-Skript, das eine Tar-Datei (möglicherweise mit gzip oder bzip2 komprimiert) in der Standardeingabe liest und eine mit bzip2 komprimierte Tar-Datei in der Standardausgabe schreibt. Die Elemente aus der Quelle werden kopiert, wenn sie mit dem an das Skript übergebenen Argument beginnen.

#!/usr/bin/env python2
import sys, tarfile
source = tarfile.open(fileobj=sys.stdin)
destination = tarfile.open(fileobj=sys.stdout, mode='w:bz2')
for info in source:
    if info.name.startswith(sys.argv[1]):
        destination.addfile(info)
destination.close()

Aufruf als

tar_filter <some.tar.gz subdir/ | ssh example.com tar -xj
Gilles 'SO - hör auf böse zu sein'
quelle
1
bsdtar (basierend auf libarchive) kann tar-Archive im laufenden Betrieb filtern, siehe meine Antwort.
Peter Cordes
Die Aufgabe bestand darin, Daten aus einem Firmware-Image zu extrahieren, daher sind Eigentümerschaft / Gruppenmitgliedschaft in der Tat wichtig. Der Python-Ansatz könnte jedoch funktionieren.
Lekensteyn
0

Ein alternativer Ansatz ohne Berechtigungen besteht darin, mit dem fakerootProgramm so zu tun, als dürften Sie den Besitzer wechseln. Während andere tar-Attribute verloren gehen, bleiben Modus, mtime und uid / gid erhalten. Diese Befehle erstellen ein temporäres Verzeichnis, extrahieren eine Teilmenge der Dateien und erstellen schließlich ein neues Archiv:

mkdir tmp
<some.tar.gz \
fakeroot -- sh -c 'cd tmp && tar -xzf- subdir/ && tar -czf- subdir' |
   ssh remote@system tar -xzvf-
rm -rf tmp
Lekensteyn
quelle
0

GNU tarhat eine --deleteOption:

$ tar -c a b c | tar --delete a | tar -t
b
c

Auf diese Weise können Sie eine Teilmenge des Eingabetars abrufen, indem Sie angeben, was nicht in der Ausgabe enthalten sein soll.

Leider konnte ich nicht mit der --excludeOption arbeiten. --deleteEs scheint daher, dass Sie zuerst eine explizite Liste ( -t) der zu löschenden Elemente abrufen und diese dann an einen anderen Aufruf von übergeben müssen tar.

$ tar --delete --no-recursion `tar -t --exclude subdir <some.tar` <some.tar | ssh ...

Oder Sie können die Liste in einer externen Datei speichern, wenn sie zu lang oder zu komplex ist:

$ tar -t --exclude subdir <some.tar >to_delete.lst
$ tar --delete --no-recursion -T to_delete.lst <some.tar | ssh ...
Karel Vlk
quelle
-1

Soweit ich weiß, kann der tarBefehl das tar-Format nicht sowohl als Eingabe als auch als Ausgabe verwenden. Sie werden Ihre Dateien lokal irgendwie extrahieren müssen, und die Verwendung Teer wieder ein tarfile on-the-fly, mit so etwas zu schaffen (die -Mittel standart Eingabe / Ausgabe anstelle einer Datei verwendet wird):

tar cf - subdir/ | ssh remote@system 'cd extractdir && tar xvf -'

Beachten Sie, dass tardie Möglichkeit, eine Tarfile direkt in eine andere Tarfile zu extrahieren, eine interessante Idee ist ...

Uriel
quelle
Ohne root gehen alle Eigentums- / Gruppeninformationen verloren, die ich ausdrücklich behalten möchte.
Lekensteyn
1
Sie sollten Ihre Frage so bearbeiten, dass Sie auf Ihrem Host keinen Root-Zugriff haben.
Uriel