Python的zipfile模块读取文件权限属性

在工作中遇到的一个问题,如何读取 zip 压缩包内,带权限属性的文件列表?例如我有一个 744 权限的文件,想要添加到 zip 包中,同时想修改它的权限为 755,网上找到的代码实现为:

buffer = "path/filename.zip"  # zip filename to write (or file-like object)
name = "folder/data.txt"      # name of file inside zip 
bytes = "blah blah blah"      # contents of file inside zip

zip = zipfile.ZipFile(buffer, "w", zipfile.ZIP_DEFLATED)
info = zipfile.ZipInfo(name)
info.external_attr = 0o755 << 16L # give full access to included file
zip.writestr(info, bytes)
zip.close()

其中设置 zip 包内文件属性的关键代码是这一行:

info.external_attr = 0o755 << 16L 

在 Python 的官方文档中,没有 external_attr 值的说明,只能从网上查找资料。

假如现在已经有一个 zip 包,我想要列出包内文件,以及文件的权限属性如 755,这时候就需要对 external_attr 进行翻译,所以有必要知道它的值构成原理。

external_attr 值是一个 32 位的整数,组成部分如下:

TTTTsstrwxrwxrwx0000000000ADVSHR
^^^^____________________________ file type as explained above
    ^^^_________________________ setuid, setgid, sticky
       ^^^^^^^^^________________ permissions
                ^^^^^^^^________ This is the "lower-middle byte" your post mentions
                        ^^^^^^^^ DOS attribute bits

其中前面 16 位是表示 Unix 系统下面的文件权限信息,后 16 位是其他平台如 Dos 的信息,所以我们只需要关注前 16 位,也就是为什么上面的 Python 代码会有 << 16 操作。

前四位表示的是文件类型,文件类型可选值如下源代码,注意这是 C 语言代码,值为 8 进制表示:

#define S_IFIFO  0010000  /* named pipe (fifo) */
#define S_IFCHR  0020000  /* character special */
#define S_IFDIR  0040000  /* directory */
#define S_IFBLK  0060000  /* block special */
#define S_IFREG  0100000  /* regular */
#define S_IFLNK  0120000  /* symbolic link */
#define S_IFSOCK 0140000  /* socket */

external_attr 值前四位 TTTT 用 8 进制表示就需要 2 位,所以取了上表中第2、3位,例如普通文件为 0o10,文件夹为 0o04,软链接为 0o12。TTTT 之后 3 位 sst 一般为 000,再后面 9 位就是权限信息,可用 3 位 8 进制表示,如 644。

所以,我们要提取权限信息,就需要将 external_attr 先右移 16 位,再转成 8 进制,最后三个数字就是了。

现在创建一个压缩包测试一下,包含的文件列表如下图:

读取 zip 文件列表信息代码:

import zipfile

if __name__ == '__main__':
    filepath = 'zipfile.zip'
    zf = zipfile.ZipFile(filepath, mode='r')
    for f in zf.infolist():
        print(f"{f.filename}: {f.external_attr}, {oct(f.external_attr>>16)}")

运行结果:

> python zip.py 
dir/: 1106051072, 0o40755
exefile.exe: 2179792896, 0o100755
regular.txt: 2175008768, 0o100644
symbolic: 2716663808, 0o120755

参考链接: