Перезаписывание ранее извлеченных файлов вместо создания новых

Существует несколько библиотек, используемых для извлечения архивных файлов через 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:\")

Это, похоже, решает мою проблему.