Python zipfile从二进制文件中删除执行权限
不知道为什么会发生这种情况,但当我在命令行运行解压文件(例如apache-groovy-binary-2.4.7.zip)时Python zipfile从二进制文件中删除执行权限,python,python-2.7,Python,Python 2.7,不知道为什么会发生这种情况,但当我在命令行运行解压文件(例如apache-groovy-binary-2.4.7.zip)时 目录为rwx-r-xr-x 文件为rwxr-xr-x或rw-r--r-- 但是当我在同一个文件上从Python 2.7脚本运行zipfile.extractall()时 目录为rwx-r-x--- 所有文件都是rw-r---——甚至是上面提到的应该是可执行文件的文件 我的umask设置为0027-这部分解释了发生的情况,但为什么要从所有文件中删除可执行位 要使Py
- 目录为
rwx-r-xr-x
- 文件为
或rwxr-xr-x
rw-r--r--
zipfile.extractall()
时
- 目录为
rwx-r-x---
- 所有文件都是
——甚至是上面提到的应该是可执行文件的文件rw-r---
umask
设置为0027
-这部分解释了发生的情况,但为什么要从所有文件中删除可执行位
要使Python采用与命令行版本类似的行为(当然除了脱壳!),最简单的修复方法是什么?原因可以在
zipfile.py
中的\u extract\u member()
方法中找到,它只调用shutil.copyfileobj()
它将写入输出文件,而不包含任何执行位
解决此问题的最简单方法是对ZipFile
进行子类化并更改extract()
(或在扩展版本中进行修补。默认情况下:
def extract(self, member, path=None, pwd=None):
"""Extract a member from the archive to the current working directory,
using its full name. Its file information is extracted as accurately
as possible. `member' may be a filename or a ZipInfo object. You can
specify a different directory using `path'.
"""
if not isinstance(member, ZipInfo):
member = self.getinfo(member)
if path is None:
path = os.getcwd()
return self._extract_member(member, path, pwd)
最后一行应更改为基于原始属性实际设置模式。您可以通过以下方式进行设置:
import os
from zipfile import ZipFile, ZipInfo
class MyZipFile(ZipFile):
def extract(self, member, path=None, pwd=None):
if not isinstance(member, ZipInfo):
member = self.getinfo(member)
if path is None:
path = os.getcwd()
ret_val = self._extract_member(member, path, pwd)
attr = member.external_attr >> 16
os.chmod(ret_val, attr)
return ret_val
with MyZipFile('test.zip') as zfp:
zfp.extractall()
(以上内容基于Python3.5,并假设zipfile名为
test.zip
)正如Rafael Almeida所指出的,extractall在python 3.6上不起作用。一个简单的解决方法是重写extractall方法,以便它调用extract而不是_extract_member。不干净,但在ZipFile有更全面的解决方案之前一直工作
class MyZipFile(ZipFile):
def extract(self, member, path=None, pwd=None):
if not isinstance(member, ZipInfo):
member = self.getinfo(member)
if path is None:
path = os.getcwd()
ret_val = self._extract_member(member, path, pwd)
attr = member.external_attr >> 16
if attr != 0:
os.chmod(ret_val, attr)
return ret_val
def extractall(self, path=None, members=None, pwd=None):
if members is None:
members = self.namelist()
if path is None:
path = os.getcwd()
else:
path = os.fspath(path)
for zipinfo in members:
self.extract(zipinfo, path, pwd)
这适用于python 3.6:
from zipfile import ZipFile, ZipInfo
class ZipFileWithPermissions(ZipFile):
""" Custom ZipFile class handling file permissions. """
def _extract_member(self, member, targetpath, pwd):
if not isinstance(member, ZipInfo):
member = self.getinfo(member)
targetpath = super()._extract_member(member, targetpath, pwd)
attr = member.external_attr >> 16
if attr != 0:
os.chmod(targetpath, attr)
return targetpath
不确定Python的实现是否支持Zip的文件集属性处理,但您应该朝这个方向看。它的工作原理如下:哦!这很好:在python2.7上测试。谢谢!@RCross,我建议您点击“答案”选中此项。根据中描述此问题的注释,在调用chmod()之前,我添加了一个检查
attr
是否为非零。这在python 3.6中不再有效,因为它调用了_extract_member而不是extract