macOS下有无非轮询方案让Shell脚本监听屏幕锁定/解锁状态?
替代轮询的macOS屏幕锁定状态监听方案
你现有的RunWhenUnlocked.sh通过轮询ioreg和PlistBuddy检测屏幕锁定状态,虽然能实现功能,但轮询会持续占用系统资源。下面提供几种无需轮询、通过系统原生钩子监听状态变化的方案:
1. 利用launchd的会话管理(最简便)
macOS的launchd可以直接根据用户图形会话的激活/停用状态自动启动或终止脚本,完全不需要轮询。
创建一个launchd配置文件~/Library/LaunchAgents/com.example.WakeTracker.plist:
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> <plist version="1.0"> <dict> <key>Label</key> <string>com.example.WakeTracker</string> <!-- 替换为你的WakeTracker.sh的绝对路径 --> <key>ProgramArguments</key> <array> <string>/full/path/to/WakeTracker.sh</string> </array> <key>RunAtLoad</key> <true/> <key>KeepAlive</key> <dict> <key>SuccessfulExit</key> <false/> <!-- 仅在Aqua图形会话激活(屏幕解锁)时保持脚本运行 --> <key>SessionType</key> <string>Aqua</string> </dict> </plist>
加载配置并启动服务:
launchctl load ~/Library/LaunchAgents/com.example.WakeTracker.plist
当屏幕锁定时,系统会自动终止WakeTracker.sh;解锁时则重新启动它,完全由系统原生机制驱动,没有轮询开销。
2. AppleScript监听系统事件
用AppleScript可以直接监听登录窗口的可见性变化,对应屏幕锁定/解锁状态,比轮询系统属性更轻量:
on handleScreenLock() do shell script "killall WakeTracker.sh 2>/dev/null" end handleScreenLock on handleScreenUnlock() do shell script "/full/path/to/WakeTracker.sh &" end handleScreenUnlock tell application "System Events" repeat set loginWindowVisible to visible of login window if loginWindowVisible then handleScreenLock() -- 等待解锁完成 repeat while visible of login window delay 0.5 end repeat handleScreenUnlock() end if delay 1 end repeat end tell
将脚本保存为.scpt文件后运行,它会在后台监听状态变化,仅在状态切换时执行操作。
3. Swift调用Cocoa通知API(最精准)
通过Swift调用macOS的DistributedNotificationCenter,可以直接监听系统发出的屏幕锁定/解锁通知,完全消除轮询:
创建LockListener.swift文件:
import Cocoa class LockObserver: NSObject { override init() { super.init() let notificationCenter = DistributedNotificationCenter.default() // 监听锁定通知 notificationCenter.addObserver( self, selector: #selector(onScreenLocked), name: NSNotification.Name("com.apple.screenIsLocked"), object: nil ) // 监听解锁通知 notificationCenter.addObserver( self, selector: #selector(onScreenUnlocked), name: NSNotification.Name("com.apple.screenIsUnlocked"), object: nil ) } @objc private func onScreenLocked() { let killTask = Process() killTask.executableURL = URL(fileURLWithPath: "/usr/bin/killall") killTask.arguments = ["WakeTracker.sh", "-q"] try? killTask.run() } @objc private func onScreenUnlocked() { let startTask = Process() startTask.executableURL = URL(fileURLWithPath: "/full/path/to/WakeTracker.sh") try? startTask.run() } } // 启动监听 let observer = LockObserver() NSApplication.shared.run()
编译为可执行文件:
swiftc LockListener.swift -o LockListener
运行这个可执行文件后,它会在后台静默监听系统通知,只有当屏幕锁定/解锁事件发生时才执行对应操作,完全没有轮询开销。
内容的提问来源于stack exchange,提问作者merlin2011
相关产品推荐
相关产品推荐

