我有一个脚本,可以创建包含符号链接的目录的 zip 文件。我 惊讶地发现 zip 文件已经压缩了目标 链接而不是链接本身,这就是我想要的并且 预期的。有人知道如何获取 zipfile 来压缩链接吗?
可以让 zip 文件存储符号链接,而不是文件本身。有关示例,请参阅此处。脚本的相关部分是将符号链接属性存储在 zipinfo 中:
zipInfo = zipfile.ZipInfo(archiveRoot)
zipInfo.create_system = 3
# long type of hex val of '0xA1ED0000L',
# say, symlink attr magic...
zipInfo.external_attr = 2716663808L
zipOut.writestr(zipInfo, os.readlink(fullPath))
zipfile
似乎不支持存储符号链接。将它们存储在 ZIP 中的方式实际上不是格式的一部分并且仅在某些实现中作为自定义扩展可用。特别是,Info-ZIP 的实现支持它们,因此您可以委托给它。确保您的解压软件可以处理此类档案 - 正如我所说,此功能尚未标准化。
请找到完整的 Python 代码作为工作示例,该示例创建一个
cpuinfo.zip
存档,其中符号链接 cpuinfo.txt
指向 /proc/cpuinfo
。
#!/usr/bin/python
import stat
import zipfile
def create_zip_with_symlink(output_zip_filename, link_source, link_target):
zipInfo = zipfile.ZipInfo(link_source)
zipInfo.create_system = 3 # System which created ZIP archive, 3 = Unix; 0 = Windows
unix_st_mode = stat.S_IFLNK | stat.S_IRUSR | stat.S_IWUSR | stat.S_IXUSR | stat.S_IRGRP | stat.S_IWGRP | stat.S_IXGRP | stat.S_IROTH | stat.S_IWOTH | stat.S_IXOTH
zipInfo.external_attr = unix_st_mode << 16 # The Python zipfile module accepts the 16-bit "Mode" field (that stores st_mode field from struct stat, containing user/group/other permissions, setuid/setgid and symlink info, etc) of the ASi extra block for Unix as bits 16-31 of the external_attr
zipOut = zipfile.ZipFile(output_zip_filename, 'w', compression=zipfile.ZIP_DEFLATED)
zipOut.writestr(zipInfo, link_target)
zipOut.close()
create_zip_with_symlink('cpuinfo.zip', 'cpuinfo.txt', '/proc/cpuinfo')
您可以进一步发出以下命令(例如在 Ubuntu 下)来查看存档如何解压到工作符号链接:
unzip cpuinfo.zip
ls -l cpuinfo.txt
cat cpuinfo.txt
我在 Zip 支持类中定义了以下方法
def add_symlink(self, link, target, permissions=0o777):
self.log('Adding a symlink: {} => {}'.format(link, target))
permissions |= 0xA000
zi = zipfile.ZipInfo(link)
zi.create_system = 3
zi.external_attr = permissions << 16
self.zip.writestr(zi, target)
虽然不是 POSIX 标准的一部分,但许多 zip 实现支持在条目上存储通用文件系统属性。 4字节值的高字节代表文件模式。
本质上你需要复制
ZipInfo.from_file
,但不要点击链接或截断模式:
st = os.lstat(path)
mtime = time.localtime(st.st_mtime)
info = zipfile.ZipInfo(name, mtime[0:6])
info.file_size = st.st_size
info.external_attr = st.st_mode << 16
out_zip.writestr(info, os.readlink(path))
以下是我尝试改进的地方:
import zipfile
import stat
import os
def archive(source, output_path):
def _convert_attr_to_symlink_type(external_attr):
# Refer to https://unix.stackexchange.com/a/14727
# zipfile external_attr is 32 bit file attribute structure
# first 4 bits determine filetype
# next 3 bit setuid, setgid, sticky
# next 9 bit is the read write execute permission for user group & others.
# next 8 bit is unused
# last 8 bit is DOS attribute
# Preserve everything except the first 4 bits (i.e filetype bit)
# MASK: 00001111111111111111111111111111
preserve_mask = (1 << 28) - 1
external_attr &= preserve_mask
# Overwrite File type as Symbolic Link File type (modify first 4 bits)
# MASK: 10100000000000000000000000000000
overwrite_mask = stat.S_IFLNK << 16
external_attr |= overwrite_mask
return external_attr
with zipfile.ZipFile(output_path, mode='w') as zf:
for root, folders, files in os.walk(source):
for folder in folders:
folderpath = os.path.join(root, folder)
if os.path.islink(folderpath):
zip_info = zipfile.ZipInfo.from_file(folderpath)
zip_info.filename = zip_info.filename.rstrip('/')
zip_info.external_attr = _convert_attr_to_symlink_type(zip_info.external_attr)
zf.writestr(zip_info, os.readlink(folderpath))
for filename in files:
filepath = os.path.join(root, filename)
if os.path.islink(filepath):
zip_info = zipfile.ZipInfo.from_file(filepath)
zip_info.external_attr = _convert_attr_to_symlink_type(zip_info.external_attr)
zf.writestr(zip_info, os.readlink(filepath))
else:
zf.write(filepath)
archive('testfolder', test.zip')