ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

易语言中控与AppleScript驱动macOS虚拟机批量自动化运维方案

易语言中控与AppleScript驱动macOS虚拟机批量自动化运维方案 简介一套面向macOS脚本学习者的AppleScript实战资源演示如何用AppleScript完成Apple ID自动登录及家庭相关操作。方案基于易语言编写的中控框架通过TCP连接远程控制macOS 10.12虚拟机实现账号自动登录、自动打开查找、自动邀请邮箱、自动创建家庭及邀请家庭成员等系列自动化动作适合已有AppleScript基础、希望了解跨语言控制macOS自动化的开发者借鉴。压缩包共包含21个文件以txt说明文档13个为主另有易语言源码.e/.ec、可执行程序.exe、Python脚本.py、动态链接库.dll及配置文件.spec/.bak等整体大小约37.88MB。文件类型覆盖从控制端到Mac执行端的完整链路可对照文档与源码理解TCP通信和脚本调用细节。目前已有1914人学习这份资源在AppleScript中文资料稀缺的条件下这类完整实例能帮助你降低摸索成本快速掌握虚拟机环境中脚本自动化控制的实现思路。1. 从易语言中控到macOS虚拟机这套AppleScript自动登录方案的架构选型批量运维一批macOS 10.12虚拟机时最耗时的是AppleID相关的重复性GUI操作登录账号、开启查找定位、创建家庭共享、邀请成员邮箱。纯手工点一遍至少五分钟几十台虚拟机就是一下午而且每步都可能因为弹窗焦点不对而中断。这套方案解决的问题就是把这些操作从「人工点击」变成「中控下发一条指令mac虚拟机内的客户端自动执行完并回传结果」。整体架构是易语言写Windows端中控通过HP_Socket的TCP长连接控制macOS虚拟机虚拟机内用Python做socket监听实际落地操作交给osascript执行AppleScript逻辑。适合有批量macOS虚拟机测试、账号维护需求的工程师也适合想学AppleScript GUI自动化但找不到参考项目的人。2. macOS 10.12虚拟机环境与AppleScript系统事件授权机制2.1 为什么选择System Events驱动AppleID登录AppleScript驱动GUI操作有两条路径一是应用自身提供脚本字典通过tell application直接调用业务命令二是通过System Events代理模拟键盘输入和鼠标点击。AppleID登录面板和家庭共享管理界面在macOS 10.12上并没有完整的脚本字典所以只能走第二条路。System Events的本质是辅助功能API它让AppleScript可以遍历目标应用的UI元素树、读取按钮和输入框、注入keystroke键盘事件。这套机制的优点是与应用版本无关只要界面结构不变就能跑缺点是对界面布局变化敏感苹果更新系统后脚本可能需要同步调整。在10.12上系统偏好设置的UI结构相对稳定脚本写好后可以长期复用。选择10.12虚拟机还有一层原因较新的macOS对辅助功能权限管控更严格TCC数据库的授权粒度更细自动化脚本在新系统上需要处理额外的权限弹窗。10.12的授权机制相对宽松适合专注业务逻辑验证。2.2 虚拟机环境加固与辅助功能授权拿到一台新的macOS 10.12虚拟机后先做环境初始化否则脚本执行过程中会被各种系统行为打断# 关闭系统偏好设置里的屏幕锁定与节能避免脚本执行中锁屏 sudo pmset -a sleep 0 displaysleep 0 disksleep 0 # 关闭屏幕保护程序触发 defaults -currentHost write com.apple.screensaver idleTime 0 # 查看辅助功能授权状态10.12的TCC数据库 sqlite3 ~/Library/Application\ Support/com.apple.TCC/TCC.db \ select client, auth_value from access where servicekTCCServiceAccessibility;上述命令就是关闭系统休眠和屏幕锁定。pmset参数中-a表示所有电源模式生效sleep 0表示系统永不休眠displaysleep 0表示显示器不关闭disksleep 0表示硬盘不进入睡眠。10.12虚拟机内脚本操作过程中如果屏幕熄灭或系统休眠System Events注入的键盘事件会丢失任务直接卡死。sqlite3命令是检查当前哪些应用被授权了辅助功能auth_value为0表示未授权、2表示已授权。辅助功能授权的关键点在于必须授权给实际执行osascript的父进程。如果直接命令行执行osascript授权对象是终端App如果由Python子进程调用osascript则授权对象是该Python运行环境。实操中经常遇到终端里手动执行脚本正常但Python调起来就报assistive access not allowed就是授权对象没覆盖到位。解决方案是去系统偏好设置 → 安全性与隐私 → 隐私 → 辅助功能把对应的终端和Python程序都勾上。2.3 用osascript验证权限链路是否就绪写正式脚本之前先跑最小验证脚本区分问题是权限链路还是脚本语法# 测试1System Events能否正常启动并返回前台进程名 osascript -e tell application System Events to get name of first process # 测试2能否注入键盘事件会在当前焦点窗口输出hello osascript -e tell application System Events to keystroke hello # 测试3能否通过UI元素树访问系统偏好设置窗口 osascript -e tell application System Events to tell process System Preferences to get name of windows三条命令层层递进。测试1验证System Events本身能启动如果报错execution error: Not authorized to send Apple events说明授权链路没打通。测试2验证键盘事件注入权限如果没有任何输出或者报错osascript is not allowed assistive access说明辅助功能授权缺失。测试3验证System Events能读取目标应用的UI元素如果返回窗口名列表说明整个链路通畅可以进入正式脚本开发。2.4 10.12虚拟机的性能注意点虚拟机环境下的UI渲染比物理机慢不少10.12在VMware或VirtualBox里开启动画效果后窗口切换和sheet弹窗可能出现明显延迟。脚本里的delay参数要按虚拟机性能调整不能照搬物理机的值。我通常在VMware配置里关掉3D加速渲染负担更小UI响应反而更稳定。另外10.12虚拟机建议分配至少2GB内存和双核CPU否则系统偏好设置面板加载慢AppleScript轮询等待UI元素的时间会被迫拉长。3. HP_Socket TCP链路易语言与AppleScript的指令通道设计3.1 为什么用TCP长连接而不是HTTP短请求中控端要同时管理多台macOS虚拟机指令下发频率高且需要实时感知每台虚拟机的执行状态。TCP长连接天然适合这个场景连接建立后持续复用中控可以随时下发指令虚拟机端的执行状态通过同一连接实时回传。如果用HTTP短请求每次指令都要重新建立连接握手开销在批量场景下被放大而且服务端主动推送状态变更比如脚本卡死、系统弹窗很不方便。HP_Socket是易语言生态中常用的网络库底层是完成端口模型IOCP高并发连接场景下性能好。项目中用的是HPSocket4C.dll和HP_Socket_中文.ec前者是C接口的动态库后者是易语言调用封装。端口方面中控作为TCP服务端监听固定端口虚拟机内Client作为TCP客户端主动连接中控NAT或内网环境下都可以工作。3.2 易语言中控端的连接管理与指令下发易语言端核心逻辑在Client.e里代码结构大致如下.版本 2 .支持库 HPSocket .程序集 程序集_中控 .子程序 _启动窗口_创建完毕 创建TCP服务端监听9527端口最大连接数设为100 服务端句柄 HP_服务端_创建 (9527, 100) .子程序 _按钮_下发登录指令_被单击 组装协议动作|账号|密码 账号和密码从编辑框读取用竖线分隔拼接 指令文本 “login_appleid|” 编辑框_账号.内容 “|” 编辑框_密码.内容 向指定客户端连接发送数据 HP_服务端_发送 (服务端句柄, 当前连接ID, 指令文本, 取文本长度 (指令文本))HP_服务端_创建的第一个参数是监听端口第二个参数是最大并发连接数。HP_服务端_发送的参数中当前连接ID来自HP_Socket的客户端连接回调事件需要在客户端接入时存储下来后续发送指令时通过IP或虚拟机编号索引到对应连接ID。指令采用竖线分隔的纯文本协议优点是肉眼可读、便于tcpdump抓包调试缺点是账号密码里如果包含竖线字符需要转义。3.3 虚拟机端Python接收指令并调用osascript虚拟机端Client.exe实际是Python打包程序项目内带python-3.8.10-macos11.pkg安装包监听连接后核心逻辑是按行读取指令分发给对应函数import socket import subprocess HOST 0.0.0.0 PORT 9527 def exec_applescript(script_source): 执行AppleScript源码返回(退出码, 输出) proc subprocess.run( [osascript, -e, script_source], capture_outputTrue, textTrue, timeout120 ) return proc.returncode, proc.stdout.strip() def handle_login(account, password): 处理自动登录AppleID指令 script f tell application System Preferences activate reveal pane com.apple.preferences.AppleIDPrefPane end tell delay 2 tell application System Events tell process System Preferences set frontmost to true keystroke {account} delay 0.5 keystroke return delay 1.5 keystroke {password} delay 0.5 keystroke return end tell end tell return exec_applescript(script) def main(): server socket.socket(socket.AF_INET, socket.SOCK_STREAM) server.bind((HOST, PORT)) server.listen(5) while True: conn, addr server.accept() data conn.recv(4096).decode(utf-8) action, *params data.split(|) if action login_appleid: account, password params[0], params[1] code, output handle_login(account, password) conn.send(f{OK if code 0 else FAIL}:{output}.encode()) conn.close() if __name__ __main__: main()Python这一侧的职责是承接TCP数据、解析指令、拼装AppleScript源码并交给osascript -e执行。为什么用Python做这层而不是直接用AppleScript监听端口因为10.12原生的AppleScript不支持socket操作用AppleScript写网络监听会绕很大一圈而Python的socket模块是标准库代码量少且调试方便。Python 3.8.10的macOS安装包兼容10.12在旧系统上能正常工作。3.4 心跳检测与断线重连批量跑任务时一台虚拟机的系统偏好设置可能弹出了模态窗口导致osascript执行挂起或者网卡休眠导致TCP连接断开。中控端需要心跳机制来感知每台虚拟机的存活状态场景心跳策略超时阈值中控动作虚拟机正常待机每30秒发pingClient回pong60秒未收到标记在线脚本执行挂起Client进程存活但不回心跳连续3次无响应发送reset指令重启Client任务网络断开TCP重传失败触发RST内核检测移除连接等待Client重连虚拟机重启连接被重置无后台保活任务3分钟重连一次心跳的实现在Python端比较简单在main循环里额外开一个线程每隔30秒向中控发送pong中控端在HP_Socket的消息回调里收到pong就刷新该连接的最后活跃时间。心跳超时的判断在中控端做用定时器定期扫描所有连接的最后活跃时间超过阈值的就调用HP_服务端_断开连接强制清理腾出连接槽位给重新上线的虚拟机。4. AppleScript实现AppleID自动登录与查找定位的完整脚本4.1 AppleID登录页面的导航精确定位macOS 10.12系统偏好设置里AppleID信息在iCloud面板中管理苹果从10.11开始把AppleID从「用户与群组」挪到了独立的偏好面板。用reveal pane可以跳到指定面板但有个坑第一次跳转时系统会加载面板插件耗时波动很大有时2秒有时8秒。-- 跳转到AppleID偏好面板 tell application System Preferences activate reveal pane com.apple.preferences.AppleIDPrefPane end tell -- 轮询等待窗口加载完成最多等20秒 set max_wait to 20 set wait_count to 0 tell application System Events tell process System Preferences set loaded to false repeat while (not loaded) and wait_count max_wait try if exists (button iCloud of window 1) then set loaded to true end if end try if not loaded then delay 1 set wait_count to wait_count 1 end if end repeat if not loaded then error AppleID面板加载超时 end if end tell end tell轮询等待比固定delay可靠得多。button iCloud是10.12 AppleID面板左侧的功能按钮只要这个按钮存在说明面板已加载完成且UI元素树可访问。如果20秒内还没出现大概率是系统偏好设置进程卡死需要killall System Preferences后重试。4.2 自动填充账号密码并处理验证码弹窗AppleID登录的完整流程是输入账号 → 输入密码 → 等待验证码新设备首次登录会触发→ 输入验证码 → 点击信任此电脑。每一步都要处理可能的系统弹窗。on login_appleid(account, password) tell application System Events tell process System Preferences -- 输入账号并回车触发密码输入框出现 keystroke account delay 0.5 keystroke return delay 2 -- 输入密码并回车 keystroke password delay 0.3 keystroke return -- 等待验证码输入框出现 set max_wait to 30 set wait_count to 0 repeat while wait_count max_wait try if exists (text field 2 of window 1) then set verify_code to do shell script cat /tmp/verify_code.txt keystroke verify_code delay 0.3 keystroke return exit repeat end if end try delay 1 set wait_count to wait_count 1 end repeat end tell end tell end login_appleidtext field 2 of window 1是验证码输入框在UI元素树中的位置。账号输入框是text field 1密码框是secure text field 1回车后Apple ID服务端会下发验证码到受信设备验证码由中控人工读取或自动提取后写到/tmp/verify_code.txt。do shell script在AppleScript中执行shell命令这里用于读取验证码文件也是AppleScript里调系统命令的标准姿势。不做这一步的后果是验证码弹窗一直悬着后面所有keystroke事件都会落入验证码弹窗而不是预期的界面。判断验证码弹窗出现的另一个方法是用keystroke向弹窗输入时观察前台进程是否仍是System Preferences如果前置进程变了说明有系统级弹窗抢占了焦点脚本需要先处理弹窗。4.3 自动打开查找定位功能AppleID登录成功后iCloud面板会出现「查找我的Mac」复选框。打开该功能会触发系统权限弹窗AppleScript可以快速处理tell application System Preferences activate reveal pane com.apple.preferences.AppleIDPrefPane end tell delay 2 tell application System Events tell process System Preferences -- 点击iCloud标签进入iCloud功能列表 click button iCloud of window 1 delay 1.5 -- 勾选查找我的Mac try click checkbox 查找我的 Mac of window 1 on error -- 部分系统版本勾选框在不同层级 click checkbox 查找我的 Mac of scroll area 1 of window 1 end try delay 1 -- 处理定位权限弹窗 try click button 允许 of sheet 1 of window 1 end try -- 确认开启 try click button 继续 of sheet 1 of window 1 end try end tell end tellclick checkbox是勾选操作的标准用法。10.12的「查找我的Mac」勾选框在iCloud功能列表的滚动区域内不同虚拟机分辨率下UI层级可能有差异所以写了两个try分支分别点击不同层级的checkbox。sheet 1 of window 1是弹窗对象macOS的弹出式对话框在UI元素树中是窗口的子层级sheet权限弹窗的「允许」「继续」按钮都在这里。如果一直点不到勾选框一个排查技巧是获取当前窗口的完整UI元素树osascript -e tell application System Events to tell process System Preferences to get entire contents of window 1这条命令会输出窗口内所有控件的层级结构、名称和位置。虽然输出量大且不格式化但用文本编辑器搜索「查找」关键词就能定位到准确的控件路径。这是AppleScript开发调试最重要的手段。4.4 登录结果验证与回执上报脚本执行完不能盲目认为成功要回读界面状态确认登录生效。中控端发来的是一个带账号的任务回执里要有账号参数Client处理的时候要捕获值、解析、回传状态。伪造回执会导致中控统计业务量出错对后端服务造成伪数据冲击。5. 家庭邀请批量流程的AppleScript编排与虚拟机断连排查5.1 全家共享入口从偏好面板到家庭成员邀请表单家庭共享的创建和成员邀请在10.12里是一条完整链路。面板入口在iCloud账号详情里创建时需要输入家庭名称然后逐个添加成员邮箱。tell application System Preferences activate reveal pane com.apple.preferences.AppleIDPrefPane end tell delay 2 tell application System Events tell process System Preferences -- 进入iCloud详情页 click button iCloud of window 1 delay 1 -- 找到家庭共享区域点击管理 click button 管理家庭 of window 1 delay 1.5 -- 在弹窗中创建新家庭 click button 创建家庭… of sheet 1 of window 1 delay 1 -- 输入家庭名称 set family_name to 我的家庭共享 keystroke family_name delay 0.3 keystroke return delay 1.5 -- 输入成员邮箱并发送邀请 set member_emails to {member1example.com, member2example.com} repeat with email in member_emails click button 邀请 of sheet 1 of window 1 delay 0.5 keystroke email delay 0.3 keystroke return -- 等待邀请发送完成 delay 2 end repeat -- 完成并关闭 click button 完成 of sheet 1 of window 1 end tell end tellclick button 创建家庭…这行如果报错通常是因为view已经切换到了「家庭成员」页按钮名称变成了「添加成员」。另一个常见问题是sheet内嵌的输入框类型不一定需要回车触发有些版本点「邀请」按钮就能识别邮箱多一次回车反而会导致空输入被提交。处理方式是改成点击「邀请」按钮后先delay让系统读取输入框内容然后直接click button 邀请消化掉通过sublime等待网络响应后进入下一轮。5.2 多账号场景下的参数传递与状态隔离易语言中控发来的每条指令都带了独立的账号Client处理要根据账号动态生成AppleScript脚本避免不同账号间的状态残留。用易语言的账号参数替换占位符再注入AppleScriptdef build_login_script(account, password): 生成登录AppleID的AppleScript脚本 return f tell application System Preferences activate reveal pane com.apple.preferences.AppleIDPrefPane end tell delay 2 tell application System Events tell process System Preferences keystroke {account} delay 0.5 keystroke return delay 1.5 keystroke {password} delay 0.5 keystroke return end tell end tell 如果账号密码里有双引号字符串拼接生成的AppleScript会语法报错需要在EasyPython端做转义。密码特殊字符,\,|都要提前过滤字符转义后说明\AppleScript字符串内引号\\\反斜杠原样保留协议层替换为\n去掉或替换为密码不该有换行密码规范化处理后先写临时文件再在AppleScript里用read读取走文件通道通信比直接在脚本里拼参数更安全。5.3 虚拟机中控的断连排查路径批量跑了几台虚拟机后经常遇到中控上看不到某台虚拟机的连接。排查路径看三层# 第一层虚拟机内网络状态 ifconfig en0 | grep inet # 第二层Python客户端监听状态 netstat -an | grep 9527 # 第三层中控端连接状态Windows命令 netstat -ano | findstr 9527先从虚拟机内的网络层查起10.12虚拟机在NAT模式下IP可能因为DHCP租约刷新而改变中控端连接的是旧IP自然断连。看到inet有地址再看9527端口有没有LISTEN状态。Python Client没在监听通常是跑挂了查看Python日志确认崩溃原因。最后看中控端如果连接还在ESTABLISHED状态但心跳超时问题出在osascript执行挂起而不是网络。最有效的防御手段是Python端把每次osascript调用都包一层超时控制proc subprocess.run( [osascript, -e, script_source], capture_outputTrue, textTrue, timeout120 # 120秒超时防止脚本卡死 )timeout120让Python在osascript执行超过120秒后主动终止抛出的TimeoutExpired异常被外层捕获后向中控回传FAIL:timeout。AppleScript自身也可能写超时逻辑但Python层的强制超时更可靠因为它不依赖AppleScript的异常处理机制。中控收到超时回执后可以直接对该虚拟机发送reset指令杀掉残留的osascript进程并重置脚本状态。另一个容易忽略的问题是osascript进程积累。AppleScript执行时如果有未关闭的tell块进程会挂在那里不退出。批量执行后ps aux | grep osascript会看到多个残留进程它们占着System Events的授权名额导致后续脚本无法注入事件。在Python端每次执行完主动清理subprocess.run([pkill, -f, osascript], capture_outputTrue)pkill是把双刃剑要确保只匹配当前用户运行的osascript进程不加-9的话会先发SIGTERM让脚本自行退出。如果正在执行的任务交出了控制权残留进程被清掉对下一轮任务是无害的。5.4 用launchd守护实现任务自动重续虚拟机重启后中控端需要人工重新下发任务批量场景下效率太低。可以把Client做成launchd守护重启后自动拉起并连接中控?xml version1.0 encodingUTF-8? !DOCTYPE plist PUBLIC -//Apple//DTD PLIST 1.0//EN http://www.apple.com/DTDs/PropertyList-1.0.dtd plist version1.0 dict keyLabel/key stringcom.macclient.daemon/string keyProgramArguments/key array string/usr/local/bin/python3/string string/opt/macclient/client.py/string /array keyRunAtLoad/key true/ keyKeepAlive/key true/ /dict /plist将plist文件放到~/Library/LaunchAgents/或/Library/LaunchAgents/目录然后launchctl load -w。KeepAlive设为true会保证Client崩溃或被杀后自动重启RunAtLoad保证用户登录后立即启动。中控端配上断线重连逻辑虚拟机重启后30秒内Client会自动连回中控下一条任务不需要任何人工干预就能被接收执行。本文还有配套的精品资源点击获取
返回列表