Перезаписывание ранее извлеченных файлов вместо создания новых
Существует несколько библиотек, используемых для извлечения архивных файлов через Python, таких как gzip, zipfile library, rarfile, tarfile, patool и т.д. Я нашел, что одна из библиотек (patool) особенно полезна из-за ее кросс-форматирования в том смысле, что он может извлекать практически любой архив, включая самые популярные, такие как ZIP, GZIP, TAR и RAR.
Чтобы извлечь файл архива с помощью patool, это легко:
patoolib.extract_archive( "Archive.zip",outdir="Folder1")
Где "Archive.zip"
- это путь к файлу архива, а "Folder1"
- путь к каталогу, в котором будет сохранен извлеченный файл.
Добыча отлично работает. Проблема в том, что если я снова запустил один и тот же код для одного и того же архивного файла, идентичный извлеченный файл будет сохранен в той же папке, но с немного другим именем (имя файла при первом запуске, имя_файла1 на втором, filename11 на третий и т.д.
Вместо этого мне нужен код для перезаписи извлеченного файла, если файл с тем же именем уже существует в каталоге.
Эта функция extract_archive
выглядит настолько минимальной - она содержит только эти два параметра, параметр verbosity
и параметр program
, который указывает программу, с которой вы хотите извлечь архивы.
редактирует:
Ответ Низама Мохамеда подтвердил, что функция extract_archive
фактически переписывает вывод. Я выяснил, что это было частично верно - функция перезаписывает ZIP файлы, но не файлы GZ, за которыми я и работаю. Для файлов GZ функция все еще генерирует новые файлы.
редактирует
Ответ Padraic Cunningham предложен с использованием основного источника. Итак, я загрузил этот код и заменил свои старые скрипты библиотеки patool скриптами в ссылке. Вот результат:
os.listdir()
Out[11]: ['a.gz']
patoolib.extract_archive("a.gz",verbosity=1,outdir=".")
patool: Extracting a.gz ...
patool: ... a.gz extracted to `.'.
Out[12]: '.'
patoolib.extract_archive("a.gz",verbosity=1,outdir=".")
patool: Extracting a.gz ...
patool: ... a.gz extracted to `.'.
Out[13]: '.'
patoolib.extract_archive("a.gz",verbosity=1,outdir=".")
patool: Extracting a.gz ...
patool: ... a.gz extracted to `.'.
Out[14]: '.'
os.listdir()
Out[15]: ['a', 'a.gz', 'a1', 'a2']
Итак, функция extract_archive
создает новые файлы каждый раз, когда она выполняется. Файл, заархивированный под a.gz
, имеет другое имя от a
.
Ответы
Ответ 1
Как вы уже сказали, patoolib предназначен как универсальный инструмент архивации.
Различные типы архивов могут быть созданы, извлечены, протестированы, перечислены, сравнены, найдены и переупакованы с помощью patool. Преимуществом patool является его простота в обработке архивных файлов без необходимости запоминать множество программ и опций.
Поведение общего извлечения против специфического поведения извлечения
Проблема заключается в том, что extract_archive
не предоставляет возможности широко изменять базовое поведение по умолчанию для инструмента архива.
Для расширения .zip, patoolib будет использовать unzip. Вы можете получить желаемое поведение извлечения архива, передав -o в качестве опции для интерфейса командной строки. т.е. unzip -o ...
Однако это специальный параметр командной строки для распаковки, и это изменяется для каждой утилиты архива.
Например, tar предлагает опцию перезаписи, но не сокращенную командную строку, эквивалентную zip. т.е. tar --overwrite
, но tar -o
не имеет предполагаемого эффекта.
Чтобы устранить эту проблему, вы можете сделать запрос функции автору или использовать альтернативную библиотеку. К сожалению, мантра patoolib потребовала бы расширения всех функций утилиты extract, чтобы затем реализовать основные параметры команд для переопределения экстрактов.
Примеры изменений в patoolib
В patoolib.programs.unzip
def extract_zip (archive, compression, cmd, verbosity, outdir, overwrite=False):
"""Extract a ZIP archive."""
cmdlist = [cmd]
if verbosity > 1:
cmdlist.append('-v')
if overwrite:
cmdlist.append('-o')
cmdlist.extend(['--', archive, '-d', outdir])
return cmdlist
В patoolib.programs.tar
def extract_tar (archive, compression, cmd, verbosity, outdir, overwrite=False):
"""Extract a TAR archive."""
cmdlist = [cmd, '--extract']
if overwrite:
cmdlist.append('--overwrite')
add_tar_opts(cmdlist, compression, verbosity)
cmdlist.extend(["--file", archive, '--directory', outdir])
return cmdlist
Это не тривиальное изменение для обновления каждой программы, каждая программа отличается!
Обновление перезаписи обезьян
Итак, вы решили не улучшать исходный код patoolib... Мы можем перезаписать поведение extract_archive
, чтобы сначала найти существующий каталог, удалить его, а затем вызвать оригинал extract_archive
.
Вы можете включить этот код в свои модули, если этого требуют многие модули, возможно, придерживайтесь его __init__.py
import os
import patoolib
from shutil import rmtree
def overwrite_then_extract_archive(archive, verbosity=0, outdir=None, program=None):
if outdir:
if os.path.exists(outdir):
shutil.rmtree(outdir)
patoolib.extract_archive(archive, verbosity, outdir, program)
patoolib.extract_archive = overwrite_then_extract_archive
Теперь, когда мы вызываем extract_archive()
, мы имеем функциональность overwrite_then_extract_archive()
.
Ответ 2
Если функциональность не существует, ее необходимо добавить. Примером этого может быть обертывание функции одним из ваших:
import os
from shutil import rmtree
def overwriting_extract_archive(zippath, outpath, **kwargs):
if os.path.exists(outpath):
shutil.rmtree(outpath)
patoolib.extract_archive(zippath, outdir=outpath, **kwargs)
Если вы хотите проверять файл за файлом и объединять новый вывод с существующим выходом, это, конечно, становится более сложной проблемой, но если это так, как вы описываете (запустите его второй раз), это должно сработать.
Ответ 3
Перезаписывание существующих файлов при извлечении архива может привести к тому, что каталог назначения будет несовместимым, если извлечение завершится неудачно.
Удаление каталога назначения до извлечения может привести к потере файлов, если извлечение не выполняется.
Я думаю, что лучший aproach - извлечение из временного каталога и синхронизация с целевым каталогом.
Для этого решения требуется модуль dirsync
. Но dirsync
snycs, только если mtime
и ctime
по умолчанию более новые, а не размер файла.
import os
import sys
from shutil import rmtree
from patoolib import extract_archive
from dirsync import sync
archive = ''
dst_dir = ''
try:
tmp_dir = extract_archive(archive)
except Exception as e:
print('extract_archive error {}'.format(e))
sys.exit(1)
else:
try:
sync(tmp_dir,dst_dir,'sync',options=['modtime'])
except Exception as e:
print('updating {} from {} failed, error {}'.format(dst_dir,tmp_dir,e))
sys.exit(1)
else:
sys.exit(0)
finally:
if os.path.exists(tmp_dir):
rmtree(tmp_dir)
Ответ 4
Используя главный источник, если вы передадите каталог с использованием outdir, он перезапишет в том числе.gz файлы:
from patoolib import extract_archive
extract_archive("foo.tar.gz",verbosity=1,outdir=".")
Вы увидите:
patool: ... /pathto/.foo.tar.gz extracted to `.'.
Единственный способ, которым он не будет перезаписываться, - это если вы не передадите каталог, в котором извлечение второго раза вы получите что-то вроде:
...foo.tar.gz extracted to `foo-1.0.2.tar1' ...(local file exists).
Запуск из bash, 7z запрашивает каждый раз, чтобы подтвердить перезапись:
In [9]: ls
foo.gz
In [10]: from patoolib import extract_archive
In [11]: extract_archive("foo.gz",verbosity=1,outdir=".")
patool: Extracting foo.gz ...
patool: running /usr/bin/7z e -o. -- foo.gz
7-Zip [64] 9.20 Copyright (c) 1999-2010 Igor Pavlov 2010-11-18
p7zip Version 9.20 (locale=en_IE.UTF-8,Utf16=on,HugeFiles=on,4 CPUs)
Processing archive: foo.gz
Extracting foo
Everything is Ok
Size: 12
Compressed: 36
patool: ... foo.gz extracted to `.'.
Out[11]: '.'
In [12]: extract_archive("foo.gz",verbosity=1,outdir=".")
patool: Extracting foo.gz ...
patool: running /usr/bin/7z e -o. -- foo.gz
7-Zip [64] 9.20 Copyright (c) 1999-2010 Igor Pavlov 2010-11-18
p7zip Version 9.20 (locale=en_IE.UTF-8,Utf16=on,HugeFiles=on,4 CPUs)
Processing archive: foo.gz
file ./foo
already exists. Overwrite with
foo?
(Y)es / (N)o / (A)lways / (S)kip all / A(u)to rename all / (Q)uit? y
Extracting foo
Everything is Ok
Size: 12
Compressed: 36
patool: ... foo.gz extracted to `.'.
Out[12]: '.'
In [13]: extract_archive("foo.gz",verbosity=1,outdir=".")
patool: Extracting foo.gz ...
patool: running /usr/bin/7z e -o. -- foo.gz
7-Zip [64] 9.20 Copyright (c) 1999-2010 Igor Pavlov 2010-11-18
p7zip Version 9.20 (locale=en_IE.UTF-8,Utf16=on,HugeFiles=on,4 CPUs)
Processing archive: foo.gz
file ./foo
already exists. Overwrite with
foo?
(Y)es / (N)o / (A)lways / (S)kip all / A(u)to rename all / (Q)uit? y
Extracting foo
Everything is Ok
Size: 12
Compressed: 36
patool: ... foo.gz extracted to `.'.
Out[13]: '.'
In [14]: ls
foo foo.gz
Извлечение файла tar.gz:
In [1]: from patoolib import extract_archive
In [2]: for x in range(4):
extract_archive("/home/padraic/Downloads/pycrypto-2.0.1.tar.gz",verbosity=1,outdir=".")
...:
patool: Extracting /home/padraic/Downloads/pycrypto-2.0.1.tar.gz ...
patool: running /bin/tar --extract -z --file /home/padraic/Downloads/pycrypto-2.0.1.tar.gz --directory .
patool: ... /home/padraic/Downloads/pycrypto-2.0.1.tar.gz extracted to `.'.
patool: Extracting /home/padraic/Downloads/pycrypto-2.0.1.tar.gz ...
patool: running /bin/tar --extract -z --file /home/padraic/Downloads/pycrypto-2.0.1.tar.gz --directory .
patool: ... /home/padraic/Downloads/pycrypto-2.0.1.tar.gz extracted to `.'.
patool: Extracting /home/padraic/Downloads/pycrypto-2.0.1.tar.gz ...
patool: running /bin/tar --extract -z --file /home/padraic/Downloads/pycrypto-2.0.1.tar.gz --directory .
patool: ... /home/padraic/Downloads/pycrypto-2.0.1.tar.gz extracted to `.'.
patool: Extracting /home/padraic/Downloads/pycrypto-2.0.1.tar.gz ...
patool: running /bin/tar --extract -z --file /home/padraic/Downloads/pycrypto-2.0.1.tar.gz --directory .
patool: ... /home/padraic/Downloads/pycrypto-2.0.1.tar.gz extracted to `.'.
In [3]: ls
pycrypto-2.0.1/
Снова все перезаписывается, единственное объяснение, которое я вижу, - это то, что любое приложение, вызываемое для распаковки ваших файлов .gz
по умолчанию, не перезаписывает и не запрашивает, а создает новые файлы каждый раз, слегка меняя имя.
Ответ 5
Кажется, я нашел обходное решение проблемы создания новых файлов каждый раз, когда выполняется метод extract_archive
библиотеки patool
.
Следует подчеркнуть тот факт, что метод способен перезаписывать/пропускать файлы, которые ранее были извлечены для других расширений архива, но не для файлов Gun Zipped.
Я заметил, что когда извлекается любой файл Gun Zipped (.gz
), извлеченный файл имеет то же имя, что и архив, но без какого-либо расширения. Чтобы проиллюстрировать это лучше, если вы измените имя от X.gz
до Y.gz
, а затем извлечете архив, извлеченный файл будет иметь имя "Y
".
Поэтому я смог выполнить простой условный:
import os,patoolib
if "name" not in os.listdir():
patoolib.extract_archive("name.gz",outdir="C:\")
Это, похоже, решает мою проблему.