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

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>

二、问题原因分析

  1. 环境变量缺失:cron和launchd以非交互式后台模式运行,默认PATH不包含部分命令路径,导致python3、osascript等命令无法被找到。
  2. 系统权限限制:macOS的隐私权限(屏幕录制、辅助功能)默认不授予后台进程,导致screencapture、osascript无法正常执行。
  3. launchd配置错误:路径中的转义符多余,且KeepAlive与StartInterval冲突,前者会让进程持续运行,后者是定时触发,二者不能同时启用。
  4. 路径空格处理不当: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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.05 23:00:56