Come aggiungere un file enorme a un archivio ed eliminarlo in parallelo


8

Supponiamo di avere un file di 80 GB /root/bigfilesu un sistema da 100 GB e di voler inserire questo file in un archivio /root/bigarchive.tar

Ovviamente ho bisogno di eliminare questo file nello stesso momento in cui viene aggiunto nell'archivio. Da qui la mia domanda:

Come eliminare un file contemporaneamente che viene aggiunto in un archivio?

Risposte:


0

Se stai usando il tarcomando GNU , puoi usare l' --remove-filesopzione:

--remove-files

rimuovere i file dopo averli aggiunti all'archivio

tar -cvf files.tar --remove-files my_directory

5
Penso che l'OP voglia rimuovere il file nello stesso momento in cui è archiviato, quindi se --remove-files viene rimosso dopo aver aggiunto il file al .tar, non sarà utile per lui dal momento che il suo disco fisso sarebbe esaurito spazio.
Zumo de Vidrio,

6

Un archivio tar non compresso di un singolo file è costituito da un'intestazione, il file e un pad di trascinamento. Quindi il tuo problema principale è come aggiungere 512 byte di intestazione all'inizio del tuo file. Puoi iniziare creando il risultato desiderato con solo l'intestazione:

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

Quindi copia i primi 10G del tuo file. Per semplicità supponiamo che il tuo dd possa leggere / scrivere 1Gib alla volta:

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

Ora trasferiamo i dati copiati dal file originale:

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

Questo sostituisce i dati con zero sparsi che non occupano spazio sul filesystem. Continuare in questo modo, aggiungendo skip=10a al successivo dde quindi incrementando l' fallocateoffset iniziale su -o 10GiB. Alla fine aggiungi alcuni caratteri nulli per riempire il file tar finale.


Se il tuo filesystem non supporta fallocatepuoi fare qualcosa di simile, ma a partire dalla fine del file. In primo luogo copiare gli ultimi 10Gibytes del file in un file intermedio chiamato, per esempio, part8. Quindi utilizzare il truncatecomando per ridurre la dimensione del file originale. Procedere in modo simile fino a quando non si hanno 8 file ciascuno da 10Gibyte. È quindi possibile concatenare l'intestazione e part1per bigarchive.tar, quindi rimuovere part1, e quindi concatenare part2e rimuoverlo, e così via.


5

L'eliminazione di un file non fa necessariamente quello che pensi che faccia. Ecco perché nei sistemi simili a UNIX viene chiamata la chiamata di sistema unlinke non delete. Dalla pagina del manuale:

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.

Di conseguenza, finché il compressore / archiviatore di dati sta leggendo dal file, quel file rimane esistente, occupando spazio nel filesystem.


1

Come eliminare un file contemporaneamente che viene aggiunto in un archivio?

Dato il contesto, interpreterò questa domanda come:

Come rimuovere i dati dal disco immediatamente dopo la lettura, prima che sia stato letto l'intero file, in modo che vi sia spazio sufficiente per il file trasformato.

La trasformazione può essere qualsiasi cosa tu voglia fare con i dati: compressione, crittografia, ecc.

La risposta è questa:

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

In breve: leggi i dati, gettali in gzip (o qualunque cosa tu voglia fare con esso), buffer l'output in modo che siamo sicuri di leggere più di quanto scriviamo e riscriverli nel file. Questa è una versione più carina e mostra l'output durante l'esecuzione:

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

Lo esaminerò, riga per riga:

cat "$file"legge il file che si desidera comprimere. È un uso inutile di cat (UUOC) poiché la parte successiva, pv, può anche leggere il file, ma trovo che questo sia più carino.

Lo convoglia in pvcui mostra le informazioni sullo stato di avanzamento (gli -cNdice "usa una sorta di [c] ursor" e dagli un [N] ame).

Quelle pipe in gzipcui ovviamente esegue la compressione (lettura da stdin, output a stdout).

Che convoglia in un altro pv(vista del tubo).

Che convoglia dd bs=$buffer iflag=fullblock. La $buffervariabile è un numero, qualcosa come 50 megabyte. È comunque molta RAM che vuoi dedicare alla gestione sicura del tuo file (come punto dati, il buffer da 50 MB per un file da 2 GB andava bene). La iflag=fullblockdice dddi leggere fino a $bufferbyte alla connessione attraverso. All'inizio, gzip scriverà un'intestazione, quindi l'output di gzip atterrerà in questa ddriga. Quindi ddattenderà fino a quando non dispone di dati sufficienti prima di inviarlo e quindi l'input può leggere ulteriormente. Inoltre, se si hanno parti non comprimibili, il file di output potrebbe essere più grande del file di input. Questo buffer si assicura che, fino a $bufferbyte, questo non sia un problema.

Quindi andiamo in un'altra linea di visualizzazione del tubo e infine sulla nostra ddlinea di uscita . Questa riga ha of(file di output) e conv=notruncspecificato, dove notruncdice di ddnon troncare (eliminare) il file di output prima di scrivere. Quindi se hai 500 byte di Ae scrivi 3 byte di B, il file sarà BBBAAAAA...(invece di essere sostituito da BBB).

Non ho coperto le 2>/dev/nullparti e non sono necessarie. Hanno semplicemente riordinato un po dd' l'output sopprimendo il messaggio "Ho finito e ho scritto così tanti byte". Le barre rovesciate alla fine di ogni riga ( \) fanno sì che bash tratti l'intera cosa come un grande comando che si convoglia l'uno nell'altro.


Ecco uno script completo per un uso più semplice. Aneddoticamente, l'ho messo in una cartella chiamata 'gz-in-place'. Ho quindi realizzato l'acronimo che ho creato: GZIP: gnu zip sul posto. Quindi con la presente presento 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!";

Ho voglia di aggiungere un'altra linea di buffering prima di gzip, per evitare che ddscriva troppo quando la linea di buffering passa attraverso, ma con solo 50MiB buffer e 1900MB di /dev/urandomdati, sembra funzionare già comunque (gli md5sums si sono abbinati dopo la decompressione). Rapporto abbastanza buono per me.

Un altro miglioramento sarebbe il rilevamento della scrittura troppo lontano, ma non vedo come farlo senza rimuovere la bellezza della cosa e creare molta complessità. A quel punto, potresti anche renderlo un vero programma Python che fa tutto correttamente (con errori per prevenire la distruzione dei dati).

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.