Вы можете попробовать воспользоваться системным вызовом, специфичным для Linux, fallocate или эквивалентом командной строки. Режим FALLOC_FL_PUNCH_HOLE
позволяет обнулить любую заданную часть файла, и, где возможно, затронутые блоки возвращаются обратно в файловую систему, создавая разреженный файл.
Не все файловые системы поддерживают этот вызов, но ext4 поддерживает.
Идея состоит в том, что вы извлекаете файл из архива, а затем используете fallocate, чтобы обнулить часть архива, которую он занимал. Словарь zip-архива содержит смещение к началу заголовка каждого файла и сжатую длину файла в архиве.
В качестве примера концепции приведен минимальный скрипт Python3 для извлечения и выполнения системного вызова для каждого файла. Вы не должны использовать его без тщательного тестирования. Вы можете сделать то же самое в сценарии оболочки, если сможете извлечь информацию. Для меня fallocate не входил в стандартную библиотеку Python, поэтому в первой половине скрипта для ее вызова используется ctypes. Сценарию нужна библиотека Python Zipfile. Также могут потребоваться другие изменения для вашей системы. Сумма обнуления не включает размер заголовка, который не дает им стать одной смежной областью.
#!/usr/bin/python3
# https://superuser.com/a/1371106/458747
# int fallocate(int fd, int mode, off_t offset, off_t len)
import ctypes
libc = ctypes.cdll.LoadLibrary("libc.so.6")
fallocate = libc.fallocate
fallocate.argtypes = (ctypes.c_int, ctypes.c_int, ctypes.c_longlong, ctypes.c_longlong)
FALLOC_FL_PUNCH_HOLE = 2
FALLOC_FL_KEEP_SIZE = 1
import sys, subprocess, zipfile # will need zlib for compression
myzip = sys.argv[1]
fd = open(myzip,"r+")
fno = fd.fileno()
zf = zipfile.ZipFile(myzip, 'r')
for info in zf.infolist():
zf.extract(info)
# print(info.header_offset,info.compress_size)
rc = fallocate(fno, FALLOC_FL_PUNCH_HOLE|FALLOC_FL_KEEP_SIZE,
info.header_offset, info.compress_size)
if rc!=0: print("fallocate failed\n")
subprocess.call("ls -ls "+myzip,shell=True)
Я проверил его на простом zip-архиве, и вы можете увидеть в первом столбце количество блоков, используемых архивом, уменьшающихся при извлечении каждого файла:
24224 -rw-r--r-- 25562742 Oct 29 22:56 ../my.zip
23292 -rw-r--r-- 25562742 Oct 29 22:56 ../my.zip
22524 -rw-r--r-- 25562742 Oct 29 22:56 ../my.zip
21524 -rw-r--r-- 25562742 Oct 29 22:56 ../my.zip
...
2800 -rw-r--r-- 25562742 Oct 29 22:56 ../my.zip
1868 -rw-r--r-- 25562742 Oct 29 22:56 ../my.zip
880 -rw-r--r-- 25562742 Oct 29 22:56 ../my.zip
124 -rw-r--r-- 25562742 Oct 29 22:56 ../my.zip