Заменять существующие файлы только для чтения при использовании tarfile Python

Я пытаюсь использовать модуль tarfile Python для извлечения архива tar.gz.

Я бы хотел, чтобы при извлечении перезаписывались любые целевые файлы, если они уже существуют - это нормальное поведение tarfile.

Однако я попадаю в ловушку того, что некоторые файлы имеют защиту от записи (например, chmod 550).

Операция tarfile.extractall() фактически не выполняется:

IOError: [Errno 13] Permission denied '/foo/bar/file'

Если я попытаюсь удалить файлы из обычной командной строки, я смогу это сделать, мне просто нужно ответить на приглашение:

$ rm <filename>
rm: <filename>: override protection 550 (yes/no)? yes

Обычная утилита tar GNU также легко обрабатывает эти файлы - она ​​просто перезаписывает их при извлечении.

Мой пользователь является владельцем файлов, поэтому нетрудно рекурсивно изменить chmod целевые файлы перед запуском tarfile.extractall. Или я могу использовать shutil.rmtree, чтобы заранее сбить цель, это обходной путь, который я использую сейчас ... Однако это кажется немного хакерским.

Есть ли более питонический способ обработки перезаписи файлов, доступных только для чтения, в tarfile, с использованием исключений или чего-то подобного?


person victorhooi    schedule 30.08.2011    source источник


Ответы (2)


Вы можете перебирать элементы тарбола и извлекать / обрабатывать ошибки в каждом файле:

В современной версии Python я бы использовал оператор with:

import os, tarfile

with tarfile.TarFile('myfile.tar', 'r', errorlevel=1) as tar:
    for file_ in tar:
        try:
            tar.extract(file_)
        except IOError as e:
            os.remove(file_.name)
            tar.extract(file_)
        finally:
            os.chmod(file_.name, file_.mode)

Если вы не можете использовать with, просто замените блок операторов with на:

tarball = tarfile.open('myfile.tar', 'r', errorlevel=1)
for file_ in tar:

Если ваш tar-шар сжат с помощью gzip, есть быстрый способ справиться с этим:

tarfile.open('myfile.tar.gz', 'r:gz')

Было бы лучше, если бы tarfile.extractall имела возможность перезаписи.

person stderr    schedule 30.08.2011
comment
Потрясающе - отлично сработало =). Намного чище, чем просто бездумно сдуть каталог. Небольшое уточнение - вы используете with, чего я не делал. Я, вероятно, должен переключиться на это - однако, где я должен вставить, кроме ReadError, для общего tar-файла. Вложенные исключения - это плохая практика, насколько я понимаю? - person victorhooi; 30.08.2011
comment
Оператор with обрабатывает ReadError исключение, возникшее при открытии архива. В случае ошибки он также автоматически закроет файл. Если вам нужна более конкретная обработка ошибок, вы можете явным образом открыть файл на раннем этапе try/except или вы можете написать свой собственный диспетчер контекста, который обрабатывает вещи по-другому. - person stderr; 19.09.2014
comment
Таким образом, я получал ошибку отказа в доступе при извлечении .sh файла из .tar.gz, и это не было проблемой перезаписи уже существующего файла - папка dest была пустой. Я думаю, это произошло из-за атрибута исполняемого файла? Каким-то образом замена tarfile.open("1.tar.gz", "r") на tarfile.open("1.tar.gz", "r:gz") решила эту проблему. Почему?! Согласно документации, r совпадает с r: *, что для архива .gz - r: gz. - person Violet Giraffe; 16.07.2015
comment
@stderr, почему os.chmod в блоке finally в конце? Не сохранит ли извлеченный файл свои разрешения без необходимости их повторной установки? - person jpyams; 11.10.2017
comment
Хороший ответ - но разве вы не имеете в виду tar вместо tarball (в примере нет с). - person PeterS6g; 29.11.2019

Мне удалось заставить код Майка Steder работать следующим образом:

tarball = tarfile.open(filename, 'r:gz')
for f in tarball:
    try: 
        tarball.extract(f)
    except IOError as e:
        os.remove(f.name)
        tarball.extract(f)
    finally:
        os.chmod(f.name, f.mode)
person Tim Santeford    schedule 19.06.2012