使用 Python 的 tarfile 时覆盖现有的只读文件

Posted

技术标签:

【中文标题】使用 Python 的 tarfile 时覆盖现有的只读文件【英文标题】:Overwrite existing read-only files when using Python's tarfile 【发布时间】:2011-11-06 10:32:10 【问题描述】:

我正在尝试使用 Python 的 tarfile 模块来提取 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

普通的 GNU tar 实用程序也可以毫不费力地处理这些文件 - 它只是在您提取时覆盖它们。

我的用户是文件的所有者,因此在运行 tarfile.extractall 之前递归地对目标文件进行 chmod 并不难。或者我可以使用 shutil.rmtree 事先吹走目标,这是我现在正在使用的解决方法。但是,这感觉有点 hackish。

是否有更 Pythonic 的方式来处理覆盖 tarfile 中的只读文件、使用异常或类似的方法?

【问题讨论】:

【参考方案1】:

您可以遍历 tarball 的成员并提取/处理每个文件上的错误:

在现代版本的 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 有一个覆盖选项会更好。

【讨论】:

太棒了 - 效果很好 =)。比盲目地吹走目录要干净得多。小澄清-您使用的是“with”,而我没有使用。我可能应该切换到那个 - 但是,我应该在哪里为整个 tarfile 插入“除了 ReadError”。据我了解,嵌套的 except 是不好的做法? with 语句将处理打开存档时引发的ReadError 异常。出错时它也会自动关闭文件。如果您想要更具体的错误处理,您可能希望在早期 try/except 中显式打开文件,或者您可能希望编写自己的上下文管理器以不同方式处理。 所以我在从 .tar.gz 中提取 .sh 文件时收到“拒绝访问”错误,这不是覆盖现有文件的问题 - dest 文件夹是空的。我认为这是由于“可执行”属性造成的?不知何故,用tarfile.open("1.tar.gz", "r:gz") 替换tarfile.open("1.tar.gz", "r") 解决了它。为什么?!根据文档,“r”与“r:*”相同,对于 .gz 存档为“r:gz”。 @stderr 为什么 os.chmod 在末尾的 finally 块中?解压出来的文件是不是不用重新设置就可以保留权限? 很好的答案——但你不是说“tar”而不是“tarball”(在没有“with”的例子中)。【参考方案2】:

我能够让 Mike 的 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)

【讨论】:

以上是关于使用 Python 的 tarfile 时覆盖现有的只读文件的主要内容,如果未能解决你的问题,请参考以下文章

Python:如何创建目录并在必要时覆盖现有目录?

python处理文件和文件的方法(shutil,filecmp ,MD5,tarfile,zip)

使用 Python 从仅具有基本名称的 tarfile 中提取文件

如何在不添加目录层次结构的情况下使用 Python 将文件添加到 tarfile?

Python中使用tarfile压缩解压tar归档文件示例(最新+全面=强烈推荐! ! !)

无法使用 python 提取 .xz 文件“tarfile.ReadError:文件无法成功打开”