Como adicionar um arquivo enorme a um arquivo morto e excluí-lo em paralelo

8

Digamos que eu tenha um arquivo de 80 GB /root/bigfileem um sistema de 100 GB e queira colocá-lo em um arquivo /root/bigarchive.tar

Obviamente, preciso excluir este arquivo ao mesmo tempo em que é adicionado ao arquivo. Daí a minha pergunta:

Como excluir um arquivo ao mesmo tempo em que é adicionado a um arquivo morto?

user123456
fonte

Respostas:

0

Se você estiver usando o tarcomando GNU , poderá usar a --remove-filesopção:

--remove-files

remover arquivos depois de adicioná-los ao arquivo

tar -cvf files.tar --remove-files my_directory
Dababi
fonte
5
Eu acho que o OP quer remover o arquivo ao mesmo tempo em que é arquivado, portanto, se --remove-files for removido depois de adicionar o arquivo ao .tar, não será útil para ele, pois seu disco rígido ficaria sem espaço.
Zumo de Vidrio 4/17
6

Um arquivo tar não compactado de um único arquivo consiste em um cabeçalho, o arquivo e um bloco à direita. Portanto, seu principal problema é como adicionar 512 bytes de cabeçalho ao início do seu arquivo. Você pode começar criando o resultado desejado apenas com o cabeçalho:

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

Em seguida, copie os primeiros 10G do seu arquivo. Por uma questão de simplicidade, assumimos que seu dd pode ler / escrever 1Gib por vez:

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

Agora, desalocamos os dados copiados do arquivo original:

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

Isso substitui os dados por zeros esparsos que não ocupam espaço no sistema de arquivos. Continue dessa maneira, adicionando skip=10a ao próximo dde, em seguida, incrementando o fallocatedeslocamento inicial para -o 10GiB. No final, adicione alguns caracteres nulos para preencher o arquivo tar final.


Se o seu sistema de arquivos não suportar, fallocatevocê poderá fazer algo semelhante, mas começando no final do arquivo. Primeiro copie os últimos 10Gibytes do arquivo para um arquivo intermediário chamado, digamos part8,. Em seguida, use o truncatecomando para reduzir o tamanho do arquivo original. Prossiga da mesma forma até ter 8 arquivos cada um com 10Gibyte. Você pode então concatenar o cabeçalho e part1para bigarchive.tar, em seguida, remover part1, e em seguida, concatenar part2e removê-lo, e assim por diante.

meuh
fonte
5

A exclusão de um arquivo não faz necessariamente o que você pensa. É por isso que, em sistemas do tipo UNIX, a chamada do sistema é chamada unlinke não delete. Na página do 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 conseqüência, enquanto o compressor / arquivador de dados estiver lendo o arquivo, ele permanecerá, ocupando espaço no sistema de arquivos.

AlexP
fonte
1

Como excluir um arquivo ao mesmo tempo em que é adicionado a um arquivo morto?

Dado o contexto, interpretarei esta pergunta como:

Como remover dados do disco imediatamente após a leitura, antes da leitura do arquivo completo, para que haja espaço suficiente para o arquivo transformado.

A transformação pode ser qualquer coisa que você queira fazer com os dados: compactar, criptografar etc.

A resposta é esta:

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

Resumindo: leia dados, jogue-os no gzip (ou o que você quiser fazer com eles), armazene em buffer a saída, para que possamos ler mais do que escrevemos e escrevê-los novamente no arquivo. Esta é uma versão mais bonita e mostra a saída durante a execução:

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

Vou passar por isso, linha por linha:

cat "$file"lê o arquivo que você deseja compactar. É um uso inútil de gato (UUOC), já que a próxima parte, pv, também pode ler o arquivo, mas acho isso mais bonito.

Ele o canaliza para pvmostrar informações de progresso ( -cNdiz 'use algum tipo de [c] ursor' e dê a [N] ame).

Aqueles tubos nos gzipquais obviamente faz a compactação (leitura de stdin, saída para stdout).

Isso entra em outro pv(exibição de canal).

Isso entra dd bs=$buffer iflag=fullblock. A $buffervariável é um número, algo como 50 megabytes. É a quantidade de RAM que você deseja dedicar ao manuseio seguro do seu arquivo (como ponto de dados, o buffer de 50 MB para um arquivo de 2 GB foi bom). O iflag=fullblockinstrui dda ler até $bufferbytes antes de transmiti-lo. No início, o gzip escreverá um cabeçalho, portanto a saída do gzip será inserida nessa ddlinha. Em seguida dd, espere até que haja dados suficientes antes de transmiti-los, para que a entrada possa ler mais. Além disso, se você tiver peças descompactáveis, o arquivo de saída poderá ser maior que o arquivo de entrada. Esse buffer garante que, até $bufferbytes, isso não seja um problema.

Em seguida, entramos em outra linha de visualização de tubos e, finalmente, em nossa ddlinha de saída . Esta linha tem of(arquivo de saída) e conv=notruncespecificada, onde notruncindica ddpara não truncar (excluir) o arquivo de saída antes de gravar. Portanto, se você possui 500 bytes Ae escreve 3 bytes B, o arquivo será BBBAAAAA...(em vez de substituído por BBB).

Não cobri as 2>/dev/nullpeças e elas são desnecessárias. Eles apenas arrumam um pouco a saída suprimindo dda mensagem "Eu terminei e escrevi tantos bytes". As barras invertidas no final de cada linha ( \) fazem com que o bash trate a coisa toda como um grande comando que se encaixa.


Aqui está um script completo para facilitar o uso. Curiosamente, eu o coloquei em uma pasta chamada 'gz-in-place'. Percebi então o acrônimo que fiz: GZIP: gnu zip no local. Então, apresento 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!";

Eu sinto vontade de adicionar outra linha de buffer antes do gzip, para impedir que ele seja ddgravado muito longe quando a linha de buffer flui, mas com apenas 50MiB de buffer e 1900MB de /dev/urandomdados, parece que já funciona de qualquer maneira (o md5sums correspondia após a descompactação). Relação boa o suficiente para mim.

Outra melhoria seria a detecção de escrita muito longe, mas não vejo como fazer isso sem remover a beleza da coisa e criar muita complexidade. Nesse ponto, você também pode torná-lo um programa python completo que faz tudo corretamente (com falhas de segurança para impedir a destruição de dados).

Luc
fonte