当使用Python的tarfile时,如何覆盖现有的只读文件

12

我正在尝试使用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。

是否有一种更具Python风格的方式来处理tar文件中的覆盖只读文件,使用异常或类似的东西?

2个回答

11
你可以循环遍历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中插入“except ReadError”吗?据我所知,嵌套的except是不好的实践? - victorhooi
1
with语句将处理打开存档时引发的ReadError异常。如果出现错误,它还会自动关闭文件。如果您想要更具体的错误处理,可能需要在早期的try/except中显式打开文件,或者编写自己的上下文管理器以处理不同的情况。 - stderr
当我从.tar.gz中提取.sh文件时,出现了“访问被拒绝”的错误,而且这不是覆盖已存在的文件的问题——目标文件夹为空。我认为这是由于“可执行”属性引起的?不知何故,将tarfile.open("1.tar.gz", "r")替换为tarfile.open("1.tar.gz", "r:gz")解决了这个问题。为什么?根据文档,“r”与“r:*”相同,对于.gz归档文件来说是“r:gz”。 - Violet Giraffe
@ stderr 为什么最后的 finally 块中要用 os.chmod ?提取出来的文件不会保留其权限吗,无需重新设置权限吗? - jpyams
好的回答--但是您是不是指的是"tar"而不是"tarball"(在没有“with”的例子中)。 - PeterS6g

3
我成功实现了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)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接