Как добавить огромный файл в архив и удалить его параллельно


8

Скажем, у меня есть файл 80 /root/bigfileГБ в системе 100 ГБ и я хочу поместить этот файл в архив /root/bigarchive.tar

Очевидно, мне нужно удалить этот файл одновременно с добавлением его в архив. Отсюда мой вопрос:

Как удалить файл одновременно с добавлением в архив?

Ответы:


0

Если вы используете tarкоманду GNU , вы можете использовать --remove-filesопцию:

--remove-файлы

удалить файлы после добавления их в архив

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

5
Я думаю, что ОП хочет удалить файл в то же время, когда он заархивирован, поэтому, если --remove-files удаляет после добавления файла в .tar, это не будет ему полезно, так как его жесткий диск будет вне пространство.
Zumo de

6

Несжатый архив tar одного файла состоит из заголовка, файла и конечной панели. Итак, ваша принципиальная проблема заключается в том, как добавить 512 байт заголовка в начало вашего файла. Вы можете начать с создания желаемого результата только с заголовком:

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

Затем скопируйте первые 10G вашего файла. Для простоты мы предполагаем, что ваш dd может читать / записывать 1Gib одновременно:

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

Теперь мы освободили скопированные данные из исходного файла:

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

Это заменяет данные разреженными нулями, которые не занимают места в файловой системе. Продолжайте таким образом, добавляя a skip=10к следующему dd, а затем увеличивая fallocateначальное смещение до -o 10GiB. В самом конце добавьте несколько нулевых символов для дополнения окончательного tar-файла.


Если ваша файловая система не поддерживает, fallocateвы можете сделать что-то похожее, но начиная с конца файла. Сначала скопируйте последнее 10Gibytes файла в промежуточный файл с именем, скажем, part8. Затем используйте truncateкоманду, чтобы уменьшить размер исходного файла. Продолжайте аналогичным образом, пока у вас не будет 8 файлов по 10 ГБ каждый. Вы можете сцепить заголовок и part1к bigarchive.tar, а затем удалить part1, а затем конкатенация part2и удалить его, и так далее.


5

Удаление файла не обязательно делает то, что вы думаете. Вот почему в UNIX-подобных системах системный вызов называется, unlinkа не delete. Со страницы руководства:

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.

Как следствие, пока компрессор / архиватор данных читает файл, этот файл остается существующим, занимая место в файловой системе.


1

Как удалить файл одновременно с добавлением в архив?

Учитывая контекст, я буду интерпретировать этот вопрос как:

Как удалить данные с диска сразу после его чтения, до того, как будет прочитан весь файл, чтобы было достаточно места для преобразованного файла.

Преобразование может быть любым, что вы хотите сделать с данными: сжатие, шифрование и т. Д.

Ответ таков:

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

Вкратце: прочитайте данные, бросьте их в gzip (или что вы хотите с ними делать), буферизируйте вывод, чтобы мы наверняка прочитали больше, чем записали, и записали его обратно в файл. Это версия, которая красивее и показывает результат при запуске:

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

Я пройдусь по ней, строка за строкой:

cat "$file"читает файл, который вы хотите сжать. Это бесполезное использование cat (UUOC), так как следующая часть, pv, также может читать файл, но я считаю, что это красивее.

Он передает его по каналу, pvкоторый показывает информацию о прогрессе ( -cNговорит, что «используйте какой-то [c] ursor» и дайте ему [N] ame).

Те каналы, в gzipкоторые явно выполняется сжатие (чтение из stdin, вывод в stdout).

Это трубы в другое pv(представление трубы).

Это трубы в dd bs=$buffer iflag=fullblock. $bufferПеременный является число, что - то вроде 50 мегабайта. Тем не менее, это много оперативной памяти, которую вы хотите выделить для безопасной обработки вашего файла (в качестве точки данных, буфер объемом 50 МБ для файла объемом 2 ГБ вполне подойдет). iflag=fullblockГоворит ddдочитать до $bufferбайт перед тем конвейер через. В начале gzip напишет заголовок, поэтому выходные данные gzip окажутся в этой ddстроке. Затем ddбудет ждать, пока у него не будет достаточно данных, прежде чем передать его, и поэтому ввод может читать дальше. Кроме того, если у вас есть несжимаемые части, выходной файл может быть больше, чем входной файл. Этот буфер гарантирует, что до $bufferбайтов это не проблема.

Затем мы переходим в другую строку представления канала и, наконец, в нашу ddстроку вывода . Эта строка имеет of(выходной файл) и conv=notruncуказывает, где notruncговорит ddне обрезать (удалить) выходной файл перед записью. Таким образом, если у вас есть 500 байтов Aи вы пишете 3 байта B, файл будет BBBAAAAA...(вместо того, чтобы быть замененным на BBB).

Я не покрывал 2>/dev/nullчасти, и они не нужны. Они просто приводят в порядок вывод, подавляя сообщение dd«Я закончил и написал это много байтов». Обратная косая черта в конце каждой строки ( \) заставляет bash рассматривать все это как одну большую команду, которая соединяет друг с другом.


Вот полный скрипт для более легкого использования. К счастью, я положил его в папку «gz-in-place». Затем я понял аббревиатуру, которую я сделал: GZIP: GNU ZIP на месте. Итак, я представляю, 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!";

Я чувствую, что добавляю еще одну строку буферизации перед gzip, чтобы предотвратить слишком ddдлинную запись, когда строка буферизации очищается, но только с буфером 50 МБ и 1900 МБ /dev/urandomданных, похоже, все равно уже работает (md5sums совпал после распаковки). Достаточно хорошее соотношение для меня.

Другим улучшением было бы обнаружение слишком длинного письма, но я не вижу, как это сделать, не удаляя красоту вещи и не создавая много сложности. На этом этапе вы могли бы просто сделать его полноценной программой на Python, которая делает все правильно (с отказоустойчивыми файлами для предотвращения уничтожения данных).

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.