You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Mac OS X下Python调用shell脚本解压ZIP致应用损坏无法打开

运行环境
  • Mac OS X Catalina 10.15
  • Python 3.10
问题背景

基于Python开发Mac OS应用,使用Py2App打包生成MySoftware.app,需要实现自动更新逻辑:自动下载新版app,删除/Applications下的旧版本,再将新版本复制到该目录。
先后尝试了三种应用分发方案:

  • 使用packages制作pkg安装包
  • 制作dmg镜像,挂载后提取文件到/Applications目录
  • 将应用打包为zip文件,解压到/Applications目录
    以上方案手动操作均正常,但通过Python脚本自动化执行时,双击更新后的应用会弹出错误提示:You can't open the application "MySoftware" because it may be damaged or incomplete.(无法打开应用“MySoftware”,因为它可能已损坏或不完整)。

最初编写的自动更新bash脚本逻辑如下:

  • 下载包含新版MySoftware.app的zip压缩包
  • 解压压缩包
  • 删除/Applications路径下的旧版MySoftware.app
  • 通过cp -R命令将新版应用复制到/Applications目录

AutoUpdate.sh脚本内容

#!/bin/bash

echo "------------- STARTING AutoUpdate.sh -----------------"
# If MySoftware.app doesn't exist, we unzip MySoftware_MAC.zip
if [[ ! -d "MySoftware.app" ]];then
  echo "The directory MySoftware.app doesn't exist"
  if [[ -f "MySoftware_MAC.zip" ]];then
    unzip MySoftware_MAC.zip
    echo "The file MySoftware_MAC.zip has been unzipped"
  else
    curl -sSL https://www.MySoftware.co/dist/MySoftware_MAC.zip --output MySoftware_MAC.zip
    unzip MySoftware_MAC.zip
    echo "The file MySoftware_MAC.zip has been unzipped"
  fi
fi
rm -rf /Applications/MySoftware.app
echo "The folder /Applications/MySoftware.app has been removed"
#curl -sSL https://www.MySoftware.co/dist/MySoftware_MAC.zip --output MySoftware_MAC.zip
#echo "The file MySoftware_MAC.zip has been downloaded"
#unzip MySoftware_MAC.zip

cp -R MySoftware.app /Applications
echo "The folder MySoftware.app has been copied pasted to the folder /Applications"
osascript -e 'quit app "MySoftware"'
echo "The application MySoftware has been Quit"
open /Applications/MySoftware.app/Contents/MacOS/MySoftware
echo "The new application /Applications/MySoftware has been open"

直接在终端执行该shell脚本时运行完全正常,更新后的应用可正常启动,但通过Python脚本调用时会复现故障。

Python调用代码

down_url = f"http://www.MySoftware.co/dist/MySoftware_MAC.zip"
urllib.request.urlretrieve(down_url, MySoftware_MAC.zip)
down_url = f"http://www.MySoftware.co/dist/AutoUpdate.sh"
urllib.request.urlretrieve(down_url, 'AutoUpdate.sh')
os.system(f"chmod 755 'AutoUpdate.sh'")
command_run_autopupdate_shell_sript = f"""/usr/bin/osascript -e 'do shell script "/bin/bash 'AutoUpdate.sh' >> {os.environ['HOME']}/log_AutoUpdate.log" with prompt "Software need to install some programs." with administrator privileges'"""

proc = subprocess.Popen(command_run_autopupdate_shell_sript, stdout=subprocess.PIPE, stderr=subprocess.PIPE,shell=True)
(output, err) = proc.communicate()
logger.info(output.decode("utf8"))
logger.info(err.decode("utf8"))
returncode_command_install_Software = proc.wait()
logger.info(f"returncode_command_install_software : {returncode_command_install_Software}")

即使用root权限执行脚本,问题依然存在:脚本无任何错误提示,返回码为0,但更新后的应用图标显示异常,双击报损坏/不完整错误。

returncode_command_install_software : 0
故障原因

问题由三个核心问题共同导致:

  1. 相对路径失效:通过osascript提权执行shell脚本时,进程的工作目录为root用户的家目录/var/root,而非下载更新包、脚本所在的应用运行目录,脚本里用相对路径找MySoftware.app、MySoftware_MAC.zip时实际找不到文件,cp -R执行时未正确复制完整的app bundle,但因为命令本身没触发语法错误所以返回码为0。
  2. 复制命令不兼容macOS app结构:cp -R命令不会保留macOS app bundle所需的符号链接、文件权限、扩展属性,Py2App打包的应用内部存在大量框架相关的符号链接,普通复制会导致链接断裂、文件权限丢失,直接造成app结构损坏。
  3. Gatekeeper隔离属性拦截:通过网络下载的文件会被系统自动加上com.apple.quarantine(隔离)扩展属性,手动操作时系统会弹出安全提示走完校验流程,但自动化复制后直接启动时,Gatekeeper检测到未经过校验的app带隔离属性,会直接判定为损坏拦截启动。
解决方案

按以下步骤修改即可解决问题:

  1. 修正Python调用逻辑,在调用脚本时传入更新资源所在的绝对路径,避免相对路径失效。将Python里的执行命令改为先切到资源所在目录再执行脚本,示例:
import os
res_dir = os.path.abspath(os.path.dirname(__file__)) # 替换为存放下载的zip、sh脚本的实际绝对路径
command_run_autopupdate_shell_sript = f"""/usr/bin/osascript -e 'do shell script "cd {res_dir} && /bin/bash ./AutoUpdate.sh >> {os.environ['HOME']}/log_AutoUpdate.log 2>&1" with prompt "Software need to install some programs." with administrator privileges'"""
  1. 替换shell脚本里的复制命令,用macOS原生的ditto命令复制app bundle,该命令会自动保留符号链接、权限、扩展属性,把原来的cp -R MySoftware.app /Applications替换为:
ditto MySoftware.app /Applications/MySoftware.app
  1. 复制完成后移除app的隔离属性,在复制完成、启动应用前加入以下命令:
xattr -dr com.apple.quarantine /Applications/MySoftware.app
  1. 修正启动命令,不要直接调用app内部的Mach-O二进制,用系统的Launch Services启动app本体,把原来的open /Applications/MySoftware.app/Contents/MacOS/MySoftware替换为:
open /Applications/MySoftware.app

修改完成后重新打包测试,自动更新流程即可正常运行,不会再出现应用损坏的提示。


内容的提问来源于stack exchange,提问作者Gauthier Buttez

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.31 23:51:25