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

7

Digamos que eu tenha um arquivo de 80GB /root/bigfile em um sistema de 100 GB e queira colocar esse arquivo em um arquivo /root/bigarchive.tar

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

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

    
por user123456 04.01.2017 / 13:54

4 respostas

0

Se você estiver usando o comando GNU tar , você pode usar a opção --remove-files :

--remove-files

remove files after adding them to the archive

tar -cvf files.tar --remove-files my_directory
    
por 04.01.2017 / 13:59
5

A exclusão de um arquivo não necessariamente faz o que você acha que faz. É por isso que em sistemas semelhantes ao UNIX a chamada do sistema é chamada unlink e não% código%. Na página de 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, desde que o compressor / arquivador de dados esteja lendo o arquivo, esse arquivo permanece em existência, ocupando espaço no sistema de arquivos.

    
por 04.01.2017 / 14:05
5

Um arquivo tar descompactado de um único arquivo consiste em um cabeçalho, o arquivo e um painel de acompanhamento. 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

Copie os primeiros 10G do seu arquivo. Para simplificar, assumimos que o seu dd pode ler / escrever 1Gib de cada vez:

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

Nós agora desalocamos os dados copiados do arquivo original:

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

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

Se o seu sistema de arquivos não suportar fallocate , você 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 comando truncate para reduzir o tamanho do arquivo original. Proceda da mesma forma até ter 8 arquivos cada um dos 10Gibyte. Você pode concatenar o cabeçalho e part1 a bigarchive.tar , depois remover part1 e, em seguida, concatenar part2 e removê-lo, e assim por diante.

    
por 04.01.2017 / 17:36
1

How to delete a file at the same time that it is added in an archive?

Dado o contexto, interpretarei esta questão como:

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

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

A resposta é esta:

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

Resumindo: leia os dados, coloque-os no gzip (ou qualquer coisa que você queira fazer com ele), armazene a saída em buffer para que tenha certeza de ler mais do que escrevemos e grave de volta 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

Eu vou passar por isso, linha por linha:

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

Ele canaliza para pv , que mostra informações de progresso ( -cN diz a ele 'use algum tipo de [c] ursor' e dá um [N] ame).

Isso canaliza para gzip , que obviamente faz a compressão (leitura de stdin, saída para stdout).

Isso canaliza para outro pv (exibição de canal).

Isso canaliza para dd bs=$buffer iflag=fullblock . A variável $buffer é um número, algo como 50 megabytes. No entanto, é muita memória RAM que você deseja dedicar ao manuseio seguro do seu arquivo (como um ponto de dados, o buffer de 50MB para um arquivo de 2GB foi bom). O iflag=fullblock informa dd para ler até $buffer bytes antes de passar por ele. No começo, o gzip irá escrever um cabeçalho, então a saída do gzip irá aparecer nesta linha dd . Então dd esperará até que tenha dados suficientes antes de fazer a passagem, e assim a entrada pode ler mais. Além disso, se você tiver partes não compactáveis, o arquivo de saída pode ser maior que o arquivo de entrada. Esse buffer garante que, até $buffer bytes, isso não seja um problema.

Em seguida, entramos em outra linha de visualização de tubulação e, finalmente, na linha de saída dd . Esta linha tem of (arquivo de saída) e conv=notrunc especificado, onde notrunc diz dd para não truncar (excluir) o arquivo de saída antes de gravar. Portanto, se você tiver 500 bytes de A e escrever 3 bytes de B , o arquivo será BBBAAAAA... (em vez de ser substituído por BBB ).

Eu não cobri as 2>/dev/null partes e elas são desnecessárias. Eles apenas limpam um pouco a saída, suprimindo a mensagem "Eu terminei e escrevi muitos bytes" de dd . As barras invertidas no final de cada linha ( \ ) fazem o bash tratar a coisa toda como um grande comando que canaliza um para o outro.

Aqui está um script completo para facilitar o uso. Curiosamente, coloquei em uma pasta chamada 'gz-in-place'. Eu então percebi o acrônimo que eu fiz: GZIP: gnu zip in-place. Portanto, eu 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 como adicionar outra linha de buffer antes gzip, para evitar que ela grave muito quando a linha dd do buffer é liberada, mas com apenas 50MiB de buffer e 1900MB de /dev/urandom data, parece funcionar de qualquer maneira (o md5sums combinou depois de descomprimir). 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ê pode também torná-lo um programa python completo que faz tudo corretamente (com failafes para evitar a destruição de dados).

    
por 01.08.2017 / 00:40