Warum sind tar.xz-Dateien bei Verwendung der Tar-Bibliothek von Python 15-mal kleiner als bei macOS-Tar?

Warum sind tar.xz-Dateien bei Verwendung der Tar-Bibliothek von Python 15-mal kleiner als bei macOS-Tar?

Kontext

Ich komprimiere Ordner mit einer Größe von ca. 1,3 GB, die jeweils mit 1440 JSON-Dateien gefüllt sind, und stelle fest, dass es einen 15-fachen Unterschied zwischen der Verwendung des tarBefehls und der in Python integriertentarfileBibliothek auf macOS oderRaspbian 10(Buster)

Minimales funktionierendes Beispiel

Dieses Skript vergleicht beide Methoden:

#!/usr/bin/env python3

from pathlib import Path
from subprocess import call
import tarfile

fullpath = Path("/Users/user/Desktop/temp/tar/2021-03-11")
zsh_out = Path(fullpath.parent, "zsh-archive.tar.xz")
py_out = Path(fullpath.parent, "py-archive.tar.xz")

# tar using terminal
# tar cJf zsh-archive.tar.xz folderpath
call(["tar", "cJf", zsh_out, fullpath])

# tar using tarfile library
with tarfile.open(py_out, "w:xz") as tar:
    tar.add(fullpath, arcname=fullpath.stem)

# Print filesizes
print(f"zsh tar filesize: {round(Path(zsh_out).stat().st_size/(1024*1024), 2)} MB")
print(f"py tar filesize: {round(Path(py_out).stat().st_size/(1024*1024), 2)} MB")

Die Ausgabe ist:

zsh tar filesize: 23.7 MB
py tar filesize: 1.49 MB

Die von mir verwendeten Versionen sind die folgenden:

  • tarunter macOS:bsdtar 3.3.2 - libarchive 3.3.2 zlib/1.2.11 liblzma/5.0.5 bz2lib/1.0.6
  • tarauf Raspbian 10:xz (XZ Utils) 5.2.4 liblzma 5.2.4
  • tarfilePython-Bibliothek:0.9.0

Dinge, die ich versucht habe

Nach der Komprimierung habe ich beide Archive extrahiert und den resultierenden Ordner mit Folgendem verglichen:

diff -r py-archive-expanded zsh-archive-expanded

Es gab keinen Unterschied.

Wenn ich die beiden Tar-Archive direkt vergleiche, scheinen sie unterschiedlich zu sein:

➜ diff zsh-archive.tar.xz py-archive.tar.xz
Binary files zsh-archive.tar.xz and py-archive.tar.xz differ

Wenn ich die Archive mit Quicklook (und dem Betterzip-Plugin) untersuche, sehe ich, dass die Dateien im Archiv anders angeordnet sind:

Links ist zsh-archive.tar.xz, rechts ist py-archive.tar.xz:

Bildbeschreibung hier eingebenBildbeschreibung hier eingeben

Das Zsh-Archiv verwendet eine unbekannte Reihenfolge und das Python-Archiv sortiert die Dateien nach Änderungsdatum. Ich bin nicht sicher, ob das wichtig ist.

Frage

Was ist hier los? Verliere ich etwas, wenn ich die Python-Bibliothek zum Komprimieren meiner Daten verwende? Ist der 15-fache Größenunterschied ein Hinweis auf ein Problem? Oder kann ich unbesorgt die effiziente Python-Implementierung verwenden?

Antwort1

tarlibKurze Antwort: Ja, es ist sicher, Python zum Komprimieren der Daten zu verwenden , im Vergleich zu BSD geht nichts verloren tar.

Grundlegendes Problem: Sortieren

Ich denke, das zugrunde liegende Problem besteht darin, dass BSD tarund GNU tarohne Sortieroptionen die Dateien im Archiv in einer undefinierten Reihenfolge ablegen.

GNU tarhat eine --sortOption:

Sortieren Sie Verzeichniseinträge nach ORDER, was eines der folgenden ist none: , name, oder inode.
Die Standardeinstellung ist --sort=none, wodurch Archivelemente in derselben Reihenfolge gespeichert werden, wie sie vom Betriebssystem zurückgegeben wird.

GNU testentar

Um dies zu testen, habe ich GNU tarauf meinem Mac mit folgendem installiert:

brew install gnu-tar

Und dann den gleichen Ordner getarnt, allerdings mit der --sortOption:

gtar --sort='name' -cJf zsh-archive-sorted.tar.xz /Users/user/Desktop/temp/tar/2021-03-11

Das zsh-archive-sorted.tar.xzArchiv ist 1,5 MB groß und entspricht der Größe des von der Python-Bibliothek erstellten Archivs.

Verketten in sortierter Reihenfolge

Die Auswirkung der Sortierung auf die endgültige Archivgröße wird weiter demonstriert, indem zunächst alle nach Namen sortierten JSON-Dateien (die die Erstellungs-Unixzeit am Anfang haben) aneinandergereiht und dann mit BSD getarnt werden tar:

cat *.json > all.txt
tar cJf zsh-cat-archive.tar.xz all.txt

Das zsh-cat-archive.tar.xzArchiv ist ebenfalls 1,5 MB groß.

Python- tarfileSortierung

Endlich, dasDokumentation der Python TarFile.add-Funktionbestätigt, dass Python tarfilestandardmäßig sortiert:

Verzeichnisse werden standardmäßig rekursiv hinzugefügt. Dies kann vermieden werden, indem rekursiv auf False gesetzt wird. Rekursion fügt Einträge in sortierter Reihenfolge hinzu.

Warum das Sortieren wichtig ist

Ich denke, der Grund, warum die Sortierung in meinem Fall einen solchen Einfluss hat, ist folgender:

Meine JSON-Dateien enthalten die Standorte von Hunderten von Fahrzeugen. Jede Minute lese ich alle Standorte aus, aber nur wenige dieser Standorte haben von Minute zu Minute einen anderen Wert.
Durch die Sortierung der Dateien nach Namen weisen zwei aufeinanderfolgende Dateien kaum unterschiedliche Zeichen auf. Offensichtlich ist dies sehr förderlich für die Komprimierungseffizienz.

Antwort2

Versuchen Sie, die Komprimierungsstufen in der macOS-Befehlszeile einzustellen.

Ich weiß, Sie fragen sichxzaber erklärt indiese Antwort hier, bei älteren Versionen von GZip können Sie die Komprimierungsstufe mit einer Umgebungsvariablen wie dieser festlegen:

GZIP=-9 tar cf zsh-archive.tar.xz folderpath

Allerdings scheint das nur mit GZip 1.8 zu funktionieren und wird in späteren Versionen nicht mehr verwendet. Verwenden Sie stattdessen die Option -I/ --use-compress-program=COMMANDfür tar. Beachten Sie, dass diese Option unter macOS möglicherweise nicht funktioniert, aber setzen Sie sie trotzdem hier, nur für alle Fälle. Der Befehl würde sich dann ändern in:

tar -I 'gzip -9' -cf zsh-archive.tar.xz folderpath

Und ja, in diesen Beispielen wird das Archiv mit Gzip statt mit komprimiert xz, aber Sie können den Befehl ganz einfach wie folgt ändern und verwenden xz:

tar -I 'xz -9' -cf zsh-archive.tar.xz folderpath

Der xzKomprimierungsgrad reicht von -0bis, -9wobei der Standardwert ist -6; dies -9ist der höchste Komprimierungsgrad.

Beachten Sie, dass es xznicht standardmäßig auf macOS installiert ist. Um es auf macOS zu installieren, müssen Sie zuerst installierenSelbstgebrautesund dann installierenxzüber Homebrew wie folgt:

brew install xz

Antwort3

Ich frage mich, was Python zur Komprimierung verwendet

http://tukaani.org/xz/

Es verwendet wahrscheinlich die Funktionsaufrufe in liblzma.Teerwird wahrscheinlich durch den xz-Shell-Befehl weitergeleitet.

Ein kurzer Kommentar zu --sort=name:

Die Sortieroption ist eine relativ neue Erweiterung von GNU Tar und wurde in Tar Version 1.28 eingeführt.

Es darf nie in BSD Tar implementiert werden.

verwandte Informationen