Cómo agregar un archivo enorme a un archivo y eliminarlo en paralelo

8

Digamos que tengo un archivo de 80GB /root/bigfileen un sistema de 100GB y quiero poner este archivo en un archivo /root/bigarchive.tar

Obviamente necesito eliminar este archivo al mismo tiempo que se agrega al archivo. De ahí mi pregunta:

¿Cómo eliminar un archivo al mismo tiempo que se agrega a un archivo?

usuario123456
fuente

Respuestas:

0

Si está utilizando el tarcomando GNU , puede usar la --remove-filesopción:

--remove-files

eliminar archivos después de agregarlos al archivo

tar -cvf files.tar --remove-files my_directory
Dababi
fuente
55
Creo que el OP quiere eliminar el archivo al mismo tiempo que está archivado, por lo que si --remove-files elimina después de agregar el archivo al .tar, no será útil para él ya que su disco duro estaría fuera de servicio espacio.
Zumo de Vidrio
6

Un archivo tar sin comprimir de un solo archivo consta de un encabezado, el archivo y un pad final. Entonces, su problema principal es cómo agregar 512 bytes de encabezado al inicio de su archivo. Puede comenzar creando el resultado deseado solo con el encabezado:

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

Luego copie los primeros 10G de su archivo. Por simplicidad asumimos que su dd puede leer / escribir 1Gib a la vez:

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

Ahora desasignamos los datos copiados del archivo original:

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

Esto reemplaza los datos con ceros dispersos que no ocupan espacio en el sistema de archivos. Continúe de esta manera, agregando una skip=10a la siguiente ddy luego incrementando el fallocatedesplazamiento inicial a -o 10GiB. Al final, agregue algunos caracteres nul para rellenar el archivo tar final.


Si su sistema de archivos no es compatible fallocate, puede hacer algo similar, pero comenzando al final del archivo. En primer lugar copiar los últimos 10Gibytes del archivo a un archivo intermedio llamado, por ejemplo, part8. Luego use el truncatecomando para reducir el tamaño del archivo original. Proceda de manera similar hasta que tenga 8 archivos de 10Gibyte cada uno. A continuación, puede concatenar la cabecera y part1para bigarchive.tar, a continuación, quitar part1, y luego concatenar part2y eliminarlo, y así sucesivamente.

meuh
fuente
5

Eliminar un archivo no necesariamente hace lo que crees que hace. Es por eso que en sistemas similares a UNIX la llamada al sistema se llama unlinky no delete. Desde la página del manual:

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.

Como consecuencia, mientras el compresor / archivador de datos lea el archivo, ese archivo permanecerá en existencia, ocupando espacio en el sistema de archivos.

AlexP
fuente
1

¿Cómo eliminar un archivo al mismo tiempo que se agrega a un archivo?

Dado el contexto, interpretaré esta pregunta como:

Cómo eliminar datos del disco inmediatamente después de leerlo, antes de que se haya leído el archivo completo, de modo que haya suficiente espacio para el archivo transformado.

La transformación puede ser cualquier cosa que desee hacer con los datos: compresión, cifrado, etc.

La respuesta es esta:

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

En resumen: lea los datos, tírelos a gzip (o lo que quiera hacer con ellos), guarde el resultado en el búfer para asegurarnos de leer más de lo que escribimos y volver a escribirlo en el archivo. Esta es una versión más bonita y muestra resultados mientras se ejecuta:

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 revisaré, línea por línea:

cat "$file"lee el archivo que quieres comprimir. Es un uso inútil de cat (UUOC) ya que la siguiente parte, pv, también puede leer el archivo, pero creo que es más bonito.

Lo canaliza en el pvque muestra información de progreso ( -cNle dice 'usa algún tipo de [c] ursor' y dale un [N] ame).

Esa tubería en la gzipque obviamente hace la compresión (lectura de stdin, salida a stdout).

Eso se canaliza a otro pv(vista de tubería).

Eso entra dd bs=$buffer iflag=fullblock. La $buffervariable es un número, algo así como 50 megabytes. Sin embargo, es la cantidad de RAM que desea dedicar al manejo seguro de su archivo (como punto de datos, el búfer de 50 MB para un archivo de 2 GB estaba bien). Le iflag=fullblockdice ddque lea hasta $bufferbytes antes de pasarlo. Al principio, gzip escribirá un encabezado, por lo que la salida de gzip aterrizará en esta ddlínea. Luego ddesperará hasta que tenga suficientes datos antes de pasarlo, y así la entrada puede leer más. Además, si tiene partes no comprimibles, el archivo de salida puede ser más grande que el archivo de entrada. Este búfer se asegura de que, hasta $bufferbytes, esto no sea un problema.

Luego vamos a otra línea de vista de tubería y finalmente a nuestra ddlínea de salida . Esta línea tiene of(archivo de salida) y conv=notruncespecificada, donde notruncle dice a ddno truncar (eliminar) el archivo de salida antes de escribir. Entonces, si tiene 500 bytes Ay escribe 3 bytes B, el archivo será BBBAAAAA...(en lugar de ser reemplazado por BBB).

No cubrí las 2>/dev/nullpartes, y son innecesarias. Simplemente arreglan un poco la salida suprimiendo ddel mensaje "Terminé y escribí estos bytes". Las barras invertidas al final de cada línea ( \) hacen que bash trate todo como un gran comando que se conecta entre sí.


Aquí hay un script completo para un uso más fácil. Como anécdota, lo puse en una carpeta llamada 'gz-in-place'. Entonces me di cuenta del acrónimo que hice: GZIP: gnu zip in place. Por la presente les 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!";

Tengo ganas de agregar otra línea de almacenamiento en búfer antes de gzip, para evitar que se escriba demasiado lejos cuando la ddlínea de almacenamiento en búfer pasa, pero con solo 50 MiB de búfer y 1900 MB de /dev/urandomdatos, parece funcionar de todos modos (los md5sums coincidieron después de descomprimir). Buena relación para mí.

Otra mejora sería la detección de la escritura demasiado lejos, pero no veo cómo hacerlo sin eliminar la belleza de la cosa y crear mucha complejidad. En ese momento, también podría convertirlo en un programa de Python completo que lo haga todo correctamente (con seguridad para evitar la destrucción de datos).

Luc
fuente