在工作中遇到的一个问题,如何读取 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
参考链接: