Comment ajouter un énorme fichier à une archive et le supprimer en parallèle

8

Supposons que j'ai un fichier de 80 Go /root/bigfilesur un système de 100 Go et que je souhaite mettre ce fichier dans une archive /root/bigarchive.tar

J'ai évidemment besoin de supprimer ce fichier en même temps qu'il est ajouté dans l'archive. D'où ma question:

Comment supprimer un fichier en même temps qu'il est ajouté dans une archive?

user123456
la source

Réponses:

0

Si vous utilisez la tarcommande GNU , vous pouvez utiliser l' --remove-filesoption:

--remove-files

supprimer des fichiers après les avoir ajoutés à l'archive

tar -cvf files.tar --remove-files my_directory
Dababi
la source
5
Je pense que l'OP veut supprimer le fichier en même temps qu'il est archivé, donc si --remove-files supprime après avoir ajouté le fichier au .tar, cela ne lui sera pas utile car son disque dur serait hors de espace.
Zumo de Vidrio
6

Une archive tar non compressée d'un seul fichier se compose d'un en-tête, du fichier et d'un tampon de fin. Votre problème principal est donc de savoir comment ajouter 512 octets d'en-tête au début de votre fichier. Vous pouvez commencer par créer le résultat souhaité avec juste l'en-tête:

tar cf - bigfile | dd count=1 >bigarchive.tar

Copiez ensuite le premier 10G de votre fichier. Pour simplifier, nous supposons que votre dd peut lire / écrire 1Gib à la fois:

dd count=10 bs=1G if=bigfile >>bigarchive.tar

Nous désallouons maintenant les données copiées du fichier d'origine:

fallocate --punch-hole -o 0 -l 10GiB bigfile

Cela remplace les données par des zéros épars qui ne prennent pas de place sur le système de fichiers. Continuez de cette manière, en ajoutant un skip=10au suivant dd, puis en incrémentant le fallocatedécalage de départ à -o 10GiB. À la toute fin, ajoutez des caractères nuls pour compléter le fichier tar final.


Si votre système de fichiers ne prend pas en charge, fallocatevous pouvez faire quelque chose de similaire, mais en commençant à la fin du fichier. Copiez d'abord les 10 derniers Go du fichier dans un fichier intermédiaire appelé, par exemple part8. Utilisez ensuite la truncatecommande pour réduire la taille du fichier d'origine. Procédez de la même manière jusqu'à ce que vous ayez 8 fichiers de 10 Go chacun. Vous pouvez ensuite concaténer l'en-tête et part1vers bigarchive.tar, puis le supprimer part1, puis le concaténer part2et le supprimer, etc.

meuh
la source
5

La suppression d'un fichier ne fait pas nécessairement ce que vous pensez qu'il fait. C'est pourquoi dans les systèmes de type UNIX, l'appel système est appelé unlinket non delete. Depuis la page de manuel:

unlink() deletes a name from the filesystem.  If that name was the last
link to a file and no processes have the file open, the file is deleted
and the space it was using is made available for reuse.

If the name was the last link to a file but any processes still have
the file open, the file will remain in existence until  the  last  file
descriptor referring to it is closed.

Par conséquent, tant que le compresseur / archiveur de données lit le fichier, ce fichier existe, occupant de l'espace dans le système de fichiers.

AlexP
la source
1

Comment supprimer un fichier en même temps qu'il est ajouté dans une archive?

Compte tenu du contexte, je vais interpréter cette question comme:

Comment supprimer des données du disque immédiatement après leur lecture, avant la lecture du fichier complet, afin qu'il y ait suffisamment d'espace pour le fichier transformé.

La transformation peut être tout ce que vous voulez faire avec les données: compression, chiffrement, etc.

La réponse est la suivante:

<$file gzip | dd bs=$buffer iflag=fullblock of=$file conv=notrunc

En bref: lisez les données, jetez-les dans gzip (ou tout ce que vous voulez en faire), mettez en mémoire tampon la sortie afin que nous soyons sûrs de lire plus que ce que nous écrivons et de le réécrire dans le fichier. Ceci est une version plus jolie et affiche la sortie lors de l'exécution:

cat "$file" \
| pv -cN 'bytes read from file' \
| gzip \
| pv -cN 'bytes received from compressor' \
| dd bs=$buffer iflag=fullblock 2>/dev/null \
| pv -cN 'bytes written back to file' \
| dd of="$file" conv=notrunc 2>/dev/null

Je vais le parcourir ligne par ligne:

cat "$file"lit le fichier que vous souhaitez compresser. C'est une utilisation inutile de cat (UUOC) car la partie suivante, pv, peut également lire le fichier, mais je trouve cela plus joli.

Il le dirige vers des pvinformations de progression ( -cNindique qu'il «utilise une sorte de [c] urseur» et lui donne un [N] ame).

Ce canal dans gziplequel fait évidemment la compression (lecture depuis stdin, sortie vers stdout).

Ce tuyau dans un autre pv(vue tuyau).

Cela entre dd bs=$buffer iflag=fullblock. La $buffervariable est un nombre, quelque chose comme 50 mégaoctets. C'est cependant beaucoup de RAM que vous souhaitez consacrer à la gestion sûre de votre fichier (en tant que point de données, un tampon de 50 Mo pour un fichier de 2 Go était bien). Le iflag=fullblockdit ddde lire jusqu'à $bufferoctets avant de le passer. Au début, gzip écrira un en-tête, donc la sortie de gzip atterrira sur cette ddligne. Il ddattendra ensuite jusqu'à ce qu'il dispose de suffisamment de données avant de les acheminer, afin que l'entrée puisse continuer à lire. De plus, si vous avez des pièces non compressibles, le fichier de sortie peut être plus gros que le fichier d'entrée. Ce tampon s'assure que, jusqu'à $bufferoctets, ce n'est pas un problème.

Ensuite, nous entrons dans une autre ligne de vue de tuyau et enfin sur notre ddligne de sortie . Cette ligne a of(fichier de sortie) et conv=notruncspécifiée, où notruncindique de ddne pas tronquer (supprimer) le fichier de sortie avant d'écrire. Donc, si vous avez 500 octets de Aet que vous écrivez 3 octets de B, le fichier sera BBBAAAAA...(au lieu d'être remplacé par BBB).

Je n'ai pas couvert les 2>/dev/nullpièces et elles sont inutiles. Ils nettoient juste un peu la sortie en supprimant ddle message "J'ai fini et écrit ce nombre d'octets". Les barres obliques inverses à la fin de chaque ligne ( \) font que bash traite le tout comme une grosse commande qui se connecte les unes aux autres.


Voici un script complet pour une utilisation plus facile. Pour l'anecdote, je l'ai mis dans un dossier appelé «gz-in-place». J'ai alors réalisé l'acronyme que j'ai fait: GZIP: gnu zip en place. Par la présente, je présente GZIP.sh:

#!/usr/bin/env bash

### Settings

# Buffer is how many bytes to buffer before writing back to the original file.
# It is meant to prevent the gzip header from overwriting data, and in case
# there are parts that are uncompressible where the compressor might exceed
# the original filesize. In these cases, the buffer will help prevent damage.
buffer=$((1024*1024*50)) # 50 MiB

# You will need something that can work in stream mode from stdin to stdout.
compressor="gzip"

# For gzip, you might want to pass -9 for better compression. The default is
# (typically?) 6.
compressorargs=""

### End of settings

# FYI I'm aware of the UUOC but it's prettier this way

if [ $# -ne 1 ] || [ "x$1" == "x-h" ] || [ "x$1" == "x--help" ]; then
    cat << EOF
Usage: $0 filename
Where 'filename' is the file to compress in-place.

NO GUARANTEES ARE GIVEN THAT THIS WILL WORK!
Only operate on data that you have backups of.
(But you always back up important data anyway, right?)

See the source for more settings, such as buffer size (more is safer) and
compression level.

The only non-standard dependency is pv, though you could take it out
with no adverse effects, other than having no info about progress.
EOF
    exit 1;
fi;

b=$(($buffer/1024/1024));
echo "Progressing '$1' with ${b}MiB buffer...";
echo "Note: I have no means of detecting this, but if you see the 'bytes read from";
echo "file' exceed 'bytes written back to file', your file is now garbage.";
echo "";

cat "$1" \
| pv -cN 'bytes read from file' \
| $compressor $compressorargs \
| pv -cN 'bytes received from compressor' \
| dd bs=$buffer iflag=fullblock 2>/dev/null \
| pv -cN 'bytes written back to file' \
| dd of="$1" conv=notrunc 2>/dev/null

echo "Done!";

J'ai envie d'ajouter une autre ligne de mise en mémoire tampon avant gzip, pour l'empêcher d'écrire trop loin lorsque la ddligne de mise en mémoire tampon passe à travers, mais avec seulement 50 Mo de mémoire tampon et 1900 Mo de /dev/urandomdonnées, cela semble fonctionner de toute façon déjà (les sommes md5 correspondent après la décompression). Rapport assez bon pour moi.

Une autre amélioration serait la détection de l'écriture trop loin, mais je ne vois pas comment faire cela sans enlever la beauté de la chose et créer beaucoup de complexité. À ce stade, vous pourriez tout aussi bien en faire un programme python à part entière qui fait tout correctement (avec des échecs pour éviter la destruction des données).

Luc
la source