tar / bz2 komprimiert eine Datei und entfernt unkomprimiertes Original

5599
Gregg Leventhal

Gibt es eine Möglichkeit, ein Verzeichnis namens dir1 in dir1.tar.bz2 umzuwandeln, ohne das Original zu behalten? Ich muss Speicherplatz sparen und einige große Dateien komprimieren, habe aber nicht genügend Platz, um eine komprimierte Kopie und das Original aufzubewahren. Gibt es eine Möglichkeit, die vorhandene Datei direkt in ein Archiv umzuwandeln?

4

1 Antwort auf die Frage

7
jaume

tar kann das nicht, aber Sie können erreichen, was Sie wollen mit:

find dir1 -depth -print0 | xargs -0 tar --create --no-recursion --remove-file --file - | bzip2 > dir1.tar.bz2

woher:

  • find dir1 -depth -print0

    listet alle Dateien und Verzeichnisse auf dir1und listet den Inhalt des Verzeichnisses vor dem Verzeichnis auf ( -depth). Die Verwendung von -print0(und -0in weiter xargsunten) ist der Schlüssel für Verzeichnis- und Dateinamen mit eingebetteten Leerzeichen unterstützt .

  • xargs -0 tar --create --no-recursion --remove-file --file -

    erstellt ein tar-Archiv und fügt jede Datei oder jedes Verzeichnis hinzu. Das tar-Archiv wird mit der Option an die Standardausgabe gesendet --file -.

  • bzip2 > dir1.tar.bz2

    komprimiert das tar-Archiv von der Standardeingabe in eine Datei namens dir1.tar.bz2.

Der benötigte freie Speicherplatz ist die Größe der größten komprimierten Datei in,dir1 da tarbei der Verarbeitung einer Datei gewartet wird, bis die Archivierung abgeschlossen ist, bevor sie gelöscht wird. Da tarist zum Ausleiten bzip2, für einen kurzen Moment, bevor tarentfernt, es jede Datei in zwei Orten befindet: unkomprimiert im Dateisystem und in komprimierten dir1.tar.bz2.

Ich war neugierig, wie der Festplattenspeicher genutzt wurde, also machte ich dieses Experiment auf meiner Ubuntu-VM:

  1. Erstellen Sie ein Dateisystem mit 1 GB:

    $ dd if=/dev/zero of=/tmp/1gb bs=1M count=1024 $ losetup /dev/loop0 /tmp/1gb $ mkfs.ext3 /dev/loop0 $ sudo mount /dev/loop0 /tmp/mnt $ df -h Filesystem Size Used Avail Use% Mounted on /dev/loop0 1008M 34M 924M 4% /tmp/mnt 
  2. Füllen Sie das Dateisystem mit 900 1-Megabyte-Dateien:

    $ chown jaume /tmp/mnt $ mkdir /tmp/mnt/dir1 $ for (( i=0; i<900; i++ )); do dd if=/dev/urandom of=/tmp/mnt/dir1/file$i bs=1M count=1; done $ chown -R jaume /tmp/mnt $ df -h Filesystem Size Used Avail Use% Mounted on /dev/loop0 1008M 937M 20M 98% /tmp/mnt 

    Das Dateisystem ist jetzt zu 98% belegt.

  3. Kopie dir1für spätere Überprüfung erstellen:

    $ cp -a /tmp/mnt/dir1 /tmp/dir1-check 
  4. Komprimieren dir1:

    $ ls /tmp/mnt dir1 lost+found $ find /tmp/mnt/dir1 -depth -print0 | xargs -0 tar --create --no-recursion --remove-file --file - | bzip2 > /tmp/mnt/dir1.tar.bz2 $ 

    Beachten Sie, dass die Befehle ohne Fehler auf dem Gerät ausgeführt wurden.

    dir1wurde entfernt, dir1.tar.bz2existiert nur :

    $ ls /tmp/mnt dir1.tar.bz2 lost+found 
  5. Erweitern dir1.tar.bz2und vergleichen mit /tmp/dir1-check:

    $ tar --extract --file dir1.tar.bz2 --bzip2 --directory /tmp $ diff -s /tmp/dir1 /tmp/dir1-check (...) Files /tmp/dir1/file97 and /tmp/dir1-check/file97 are identical Files /tmp/dir1/file98 and /tmp/dir1-check/file98 are identical Files /tmp/dir1/file99 and /tmp/dir1-check/file99 are identical $ 

    Kopie dir1und unkomprimiert dir1.tar.bz2sind identisch!

Dies kann in einem Skript verallgemeinert werden:

  1. Erstellen Sie eine Datei mit dem folgenden Namen tarrm(oder einem beliebigen anderen Namen) mit folgendem Inhalt:

    #!/bin/bash  # This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. # This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. # You should have received a copy of the GNU General Public License along with this program. If not, see <http://www.gnu.org/licenses/>.  # dir is first argument dir="$1" # check dir exists if [ ! -d "$dir" ]; then echo "$(basename $0): error: '$dir' doesn't exist" 1>&2 exit 1 fi # check if tar file exists if [ -f "$.tar" -o -f "$.tar.bz2" ]; then echo "$(basename $0): error: '$dir.tar' or '$.tar.bz2' already exist" 1>&2 exit 1 fi  # --keep is second argument if [ "X$2" == "X--keep" ]; then # keep mode removefile="" echo " Tarring '$dir'" else removefile="--remove-file" echo " Tarring and **deleting** '$dir'" fi  # normalize directory name (for example, /home/jaume//// is a legal directory name, but will break $.tar.bz2 - it needs to be converted to /home/jaume) dir=$(dirname "$dir")/$(basename "$dir")  # create compressed tar archive and delete files after adding them to it find "$dir" -depth -print0 | xargs -0 tar --create --no-recursion $removefile --file - | bzip2 > "$.tar.bz2"  # return status of last executed command if [ $? -ne 0 ]; then echo "$(basename $0): error while creating '$.tar.bz2'" 1>&2 fi 
  2. Mach es ausführbar:

    chmod a+x tarrm

Das Skript führt einige grundlegende Fehlerüberprüfungen aus: Es dir1muss vorhanden sein dir1.tar.bz2und dir1.tarsollte nicht vorhanden sein und verfügt über einen Keep- Modus. Es unterstützt auch Verzeichnis- und Dateinamen mit eingebetteten Leerzeichen.

Ich habe das Skript getestet, kann aber nicht garantieren, dass es fehlerlos ist. Verwenden Sie es daher zunächst im Keep-Modus:

./tarrm dir1 --keep

Durch diesen Aufruf wird das Verzeichnis hinzugefügt dir1, dir1.tar.bz2das Verzeichnis wird jedoch nicht gelöscht.

Wenn Sie dem Skript vertrauen, verwenden Sie es folgendermaßen:

./tarrm dir1

Das Skript informiert Sie darüber, dass dir1es beim Teering gelöscht wird:

Tarring and **deleting** 'dir1'

Zum Beispiel:

$ ls -lF total 4 drwxrwxr-x 3 jaume jaume 4096 2013-10-11 11:00 dir 1/ $ find "dir 1" dir 1 dir 1/subdir 1 dir 1/subdir 1/file 1 dir 1/file 1 $ /tmp/tarrm dir\ 1/ Tarring and **deleting** 'dir 1/' $ echo $? 0 $ ls -lF total 4 -rw-rw-r-- 1 jaume jaume 181 2013-10-11 11:00 dir 1.tar.bz2 $ tar --list --file dir\ 1.tar.bz2  dir 1/subdir 1/file 1 dir 1/subdir 1/ dir 1/file 1 dir 1/ 
Interessanter Ansatz. Es scheint jedoch davon auszugehen, dass genügend Speicherplatz vorhanden ist, um sowohl das unkomprimierte tar-Archiv als auch das fast vollständig komprimierte Archiv zu speichern, da bzip2 (sowie andere Tools, die mir bekannt sind) dies eigentlich nicht tun * an Ort und Stelle komprimieren *. Vielleicht, nur vielleicht, könnten Sie dazu eine Pfeife aus einer Subshell verwenden? a CVn vor 11 Jahren 0
Ja, meine vorgeschlagene Lösung benötigt tatsächlich genügend Platz, um `dir1.tar 'zu komprimieren. Eine andere (viel einfachere) Vorgehensweise wäre, stattdessen `zip 'zu verwenden:` zip --recurse-path --move "dir 1.zip" "dir 1" `. Ich habe meine Antwort bearbeitet, um "zip" zu erwähnen ... jaume vor 11 Jahren 0
Nun, wie sich herausstellt, ist "zip" keine Option. Ich rollte zurück zur ursprünglichen Antwort. `zip` liefert nicht, was das OP will (von der Manpage): * - move Verschiebt die angegebenen Dateien in das zip-Archiv; Tatsächlich werden die Zielverzeichnisse / -dateien ** gelöscht, nachdem das angegebene ZIP-Archiv erstellt wurde. Wenn ein Verzeichnis nach dem Entfernen der Dateien leer wird, wird das Verzeichnis ebenfalls entfernt. ** Es werden keine Löschungen durchgeführt, bis zip das Archiv fehlerfrei erstellt hat. *** jaume vor 11 Jahren 0
Beeindruckende Lösung. Ich denke, die verbleibende Frage ist, wie viel freier Speicherplatz erforderlich ist, um diese Operation auszuführen. Benötigt es Originaldatei + Archiv oder ist es weniger als das? Gregg Leventhal vor 11 Jahren 0
@ MichaelKjörling Danke für den Hinweis, ich habe gemerkt, dass ich "tar --create" anstelle von "tar --append" verwenden könnte. Daher habe ich die Lösung verbessert, die komprimierte Ausgabe in eine Datei zu senden und zu senden. Nun ist der erforderliche freie Speicherplatz die komprimierte Größe der Datei in "dir1", die nach dem Komprimieren die größte ist, viel weniger als "dir.tar". jaume vor 11 Jahren 1
@GreggLeventhal Ich habe die Lösung verbessert. Nun ist die Menge an freiem Speicherplatz die komprimierte Größe der Datei in "dir1", die nach dem Komprimieren die größte ist. Ich machte einen Test mit einem Dateisystem, das zu 98% voll war, und arbeitete problemlos. jaume vor 11 Jahren 0
Ich habe es noch nicht selbst getestet, aber ich gebe Ihnen die Antwort, wie viel Zeit und Mühe Sie investieren. Sie sollten dieses Skript in eine höhere Programmiersprache wie Python einbinden und es öffnen. Danke für deine Arbeit! Gregg Leventhal vor 11 Jahren 0
Ich freue mich sehr über Ihre Kommentare, Gregg. Mein Python (und PHP, Perl usw.) ist ziemlich schlecht. Ich denke nicht, dass ich das Skript neu schreibe, aber ich habe die standardmäßige GPL-Berechtigungsanweisung hinzugefügt. Stellen Sie sicher, dass Sie über eine Sicherung Ihrer Daten verfügen, bevor Sie das Skript testen. jaume vor 11 Jahren 0
Wenn ich könnte, würde ich dies erneut aufstimmen, aber leider habe ich meine Zuteilung von Upvotes für diese bestimmte Antwort bereits verwendet ... a CVn vor 11 Jahren 0
Ich denke, hier fehlt etwas. Die Variable "removeir" wird nicht verwendet. Beim Testen werden nur Dateien gelöscht, jedoch keine Verzeichnisse. Ich füge einfach `if [" $ removedir "==" true "] hinzu; dann: rm -rf $ dir; fi` mveroone vor 10 Jahren 1
Sie haben recht, danke, dass Sie darauf hingewiesen haben und für Ihren Vorschlag. Ich überprüfe das Skript später und bearbeite es. jaume vor 10 Jahren 0
@Kwaio In meinen Tests habe ich herausgefunden, dass die Variable 'removeir' nicht notwendig ist, also habe ich sie entfernt. Das Verzeichnis wird gelöscht, wenn "--keep" nicht angegeben ist. Ich frage mich, warum es nur Dateien in Ihrem Test löschte. Ich habe "tarrm" unter OS X 10.9.1 ausgeführt. jaume vor 10 Jahren 0
Ich verwende RHEL3 mit Kernel 2.4.17 und coreutils 4.5.3. das kann Dinge ändern. Danke für das Skript, sowieso sind Dateien am meisten Platz. mveroone vor 10 Jahren 0