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

12 投票
2 回答
9367 浏览
提问于 2025-04-17 00:36

我正在尝试使用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之前,递归地修改目标文件的权限其实并不难。或者我可以使用shutil.rmtree先把目标文件删除,这也是我现在在用的解决办法。不过,这样做感觉有点不太优雅。

有没有更符合Python风格的方法来处理tarfile中覆盖只读文件的问题,比如使用异常处理或者其他类似的方式?

2 个回答

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)
11

你可以遍历压缩包里的每个文件,并在处理每个文件时检查错误:

在现代版本的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:

如果你的压缩包是gz格式的,有个简单的方法可以处理,只需要:

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

如果 tarfile.extractall 有一个覆盖选项,那就更好了。

撰写回答