MacOS Sonoma定时截图上传脚本自动运行故障排查求助
问题:MacOS下cron/launchd无法自动执行截图上传脚本
我不是程序员,通过ChatGPT生成了一套脚本,实现MacOS Sonoma 14.1(M1 Pro)设备每2分钟全屏截图、本地存储,并调用Python脚本上传至Google Drive。手动运行脚本一切正常,但配置cron和launchd后均无法自动执行。以下是相关脚本及配置代码:
一、现有脚本与配置
bash脚本代码
#!/bin/bash # Переход в директорию скрипта cd "/Users/evgenijserikov/Script Screenshots" # Путь к скрытой папке для сохранения скриншотов screenshot_dir="$HOME/.hidden_screenshots" # Создать папку, если она не существует mkdir -p "$screenshot_dir" # Создать уникальное имя файла для скриншота с датой, временем, именем приложения и названием веб-страницы или диалога/группы в Telegram timestamp=$(date +"%H:%M_%d-%m-%Y") app_name=$(osascript -e 'tell application (path to frontmost application as text) to name as text') # Получить название вкладки в Google Chrome или название диалога/группы в Telegram, если соответствующее приложение открыто if [ "$app_name" = "Google Chrome" ]; then window_title=$(osascript -e 'tell application "Google Chrome" to get title of active tab of front window') elif [ "$app_name" = "Telegram" ]; then telegram_dialog=$(osascript -e 'tell application "Telegram" to return name of the front chat of front window' 2>/dev/null) fi # Создать имя файла с учетом названия вкладки или диалога/группы (если есть) if [ -n "$window_title" ]; then screenshot_file="$screenshot_dir/${timestamp}_${app_name}_${window_title}.png" elif [ -n "$telegram_dialog" ]; then screenshot_file="$screenshot_dir/${timestamp}_${app_name}_${telegram_dialog}.png" else screenshot_file="$screenshot_dir/${timestamp}_${app_name}.png" fi # Сделать скриншот всего экрана и сохранить в файл screencapture -x "$screenshot_file" # Загрузить скриншот на Google Drive python3 upload_to_drive.py "$screenshot_file" # Удалить скриншоты старше 48 часов из локальной папки find "$screenshot_dir" -name "*.png" -mtime +2 -exec rm {} \;
Python脚本代码
from googleapiclient.http import MediaFileUpload from googleapiclient.discovery import build from google.oauth2.credentials import Credentials import sys from datetime import datetime, timedelta, timezone # Путь к файлу скриншота передается как аргумент screenshot_file_path = sys.argv[1] # ID вашей папки на Google Drive folder_id = '18W_MZKixax-xmgcwnSbFzm301C_4BXed' # Загрузка сохраненных учетных данных credentials = Credentials.from_authorized_user_file('/Users/evgenijserikov/Script Screenshots/token.json') # Инициализация сервиса Google Drive service = build('drive', 'v3', credentials=credentials) # Вычисление времени 48 часов назад в формате ISO 8601 с учетом часового пояса UTC time_48_hours_ago = datetime.now(timezone.utc) - timedelta(hours=48) formatted_time = time_48_hours_ago.isoformat() # Получение и удаление старых файлов из Google Drive files = service.files().list(q=f"'{folder_id}' in parents and modifiedTime < '{formatted_time}'", fields='files(id, name)').execute().get('files', []) for file in files: service.files().delete(fileId=file['id']).execute() # Создание метаданных файла file_metadata = { 'name': screenshot_file_path.split('/')[-1], 'parents': [folder_id] } # Загрузка файла media = MediaFileUpload(screenshot_file_path) file = service.files().create(body=file_metadata, media_body=media, fields='id').execute() print(f"File ID: {file.get('id')}")
crontab配置
*/2 * * * * /Users/evgenijserikov/ScriptScreenshots/screenshot_script.sh
.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.user.screenshot</string> <key>Program</key> <string>/Users/evgenijserikov/Script\ Screenshots/screenshot_script.sh</string> <key>RunAtLoad</key> <true/> <key>KeepAlive</key> <true/> <key>StartInterval</key> <integer>120</integer> </dict> </plist>
二、问题原因分析
- 环境变量缺失:cron和launchd以非交互式后台模式运行,默认PATH不包含部分命令路径,导致
python3、osascript等命令无法被找到。 - 系统权限限制:macOS的隐私权限(屏幕录制、辅助功能)默认不授予后台进程,导致
screencapture、osascript无法正常执行。 - launchd配置错误:路径中的转义符多余,且
KeepAlive与StartInterval冲突,前者会让进程持续运行,后者是定时触发,二者不能同时启用。 - 路径空格处理不当:crontab中脚本路径含空格但未正确转义或加引号,导致无法识别脚本位置。
三、修复方案
1. 调整bash脚本
- 在脚本开头添加完整PATH,确保所有命令能被找到:
#!/bin/bash export PATH="/usr/bin:/bin:/usr/sbin:/sbin:/usr/local/bin" - 给所有系统命令使用绝对路径(避免环境变量问题):
- 把
osascript改成/usr/bin/osascript - 把
screencapture改成/usr/sbin/screencapture - 把
python3改成绝对路径(可通过which python3命令查询,比如/usr/bin/python3)
- 把
- 确保脚本具备可执行权限:
chmod +x "/Users/evgenijserikov/Script Screenshots/screenshot_script.sh"
2. 修复cron配置
- 修改crontab内容,给含空格的路径加引号,并设置PATH:
PATH=/usr/bin:/bin:/usr/sbin:/sbin:/usr/local/bin */2 * * * * "/Users/evgenijserikov/Script Screenshots/screenshot_script.sh" - 授予cron权限:
打开系统设置→隐私与安全性→屏幕录制,点击+,通过“前往文件夹”输入/usr/sbin/cron并添加;同样在辅助功能中添加/usr/sbin/cron。
3. 修复launchd配置
- 修改
.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.user.screenshot</string> <key>Program</key> <string>/Users/evgenijserikov/Script Screenshots/screenshot_script.sh</string> <key>RunAtLoad</key> <true/> <key>StartInterval</key> <integer>120</integer> <!-- 添加日志记录,方便排查错误 --> <key>StandardOutPath</key> <string>/Users/evgenijserikov/Script Screenshots/launchd_out.log</string> <key>StandardErrorPath</key> <string>/Users/evgenijserikov/Script Screenshots/launchd_err.log</string> </dict> </plist> - 安装并加载plist:
# 移动plist到用户LaunchAgents目录 mv com.user.screenshot.plist ~/Library/LaunchAgents/ # 加载配置 launchctl load ~/Library/LaunchAgents/com.user.screenshot.plist - 授予launchd相关权限:
在系统设置→隐私与安全性→屏幕录制中添加/bin/bash;同样在辅助功能中添加/bin/bash。
4. 测试验证
- 对于cron:等待2分钟后检查
~/.hidden_screenshots目录是否有新截图,或查看cron日志(/var/log/cron.log)。 - 对于launchd:手动触发任务并查看日志:
launchctl start com.user.screenshot cat "/Users/evgenijserikov/Script Screenshots/launchd_err.log"
内容的提问来源于stack exchange,提问作者NewbieCoder
相关产品推荐
相关产品推荐

