Этот урок - про автоматизацию mac по-взрослому. Разберем, как устроены Apple Events (фундамент, на котором стоит почти все), чем shortcuts mac отличается от applescript, как запускать все это из терминала через osascript и shortcuts run, где доживает свой век automator, и когда вместо всего этого стоит взять Hammerspoon. Без магии: будем смотреть на команды и на их вывод.
Apple Events - фундамент, который ты не видишь
Прежде чем трогать инструменты, пойми механику. Почти вся автоматизация приложений на macOS - это Apple Events: межпроцессные сообщения, которыми один процесс просит другой что-то сделать. Когда ты пишешь "tell Finder to ..." - под капотом твой скрипт шлет Apple Event процессу Finder, тот его исполняет и возвращает результат. Это работает потому, что приложение опубликовало словарь (dictionary) - описание объектов и команд, которые оно понимает: у Finder есть files, folders, windows; у Mail - messages, accounts.
Над этим механизмом лежит OSA (Open Scripting Architecture) - абстракция, которая позволяет писать скрипты на разных языках, но слать одни и те же Apple Events. Реальных OSA-языков два: AppleScript (родной, с английскоподобным синтаксисом) и JavaScript for Automation, он же JXA (JavaScript, добавлен в OS X Yosemite). Оба компилируются в одни и те же события.
Ключевой момент 2026 года - безопасность. Apple Events между приложениями контролирует TCC (тот самый фреймворк приватности из урока про разрешения). Первый раз, когда твой скрипт обращается к чужому приложению, система спросит разрешение - "Терминал хочет управлять Finder". Без согласия прилетит ошибка -1743 (errAEEventNotPermitted). Это первая грабля любого, кто переносит скрипты на свежую машину или гоняет их по SSH.
Код: Выделить всё
$ osascript -e 'tell application "Finder" to get name of home'
# при первом запуске - системный запрос разрешения;
# если отказать или запускать без сессии (ssh):
# execution error: Not authorized to send Apple events to Finder. (-1743)

osascript: AppleScript и JXA прямо из терминала
osascript - это твой мост из shell в мир OSA. Он умеет два режима: инлайн через -e и запуск файла-скрипта. Язык переключается флагом -l (по умолчанию AppleScript).
Код: Выделить всё
# инлайн AppleScript: уведомление и диалог
$ osascript -e 'display notification "Бэкап готов" with title "Cron"'
# несколько -e склеиваются построчно
$ osascript -e 'tell application "Music"' -e 'get name of current track' -e 'end tell'
Mind Cycle
# то же самое на JXA (-l JavaScript)
$ osascript -l JavaScript -e 'Application("Music").currentTrack.name()'
Mind Cycle
Файловый скрипт удобно сделать исполняемым через шебанг - тогда он запускается как обычная команда:
Код: Выделить всё
#!/usr/bin/osascript -l JavaScript
const app = Application.currentApplication();
app.includeStandardAdditions = true;
app.displayNotification("Сборка прошла", { withTitle: "CI" });
Сочетание с shell: do shell script и обратно
Главная сила в том, что мосты двусторонние. Из AppleScript ты вызываешь shell командой do shell script, а из shell дергаешь AppleScript через osascript. Это позволяет докинуть в GUI-автоматизацию любую утилиту BSD или Homebrew.
Код: Выделить всё
$ osascript -e 'do shell script "df -h / | tail -1 | awk \"{print \\$5}\""'
73%
Два важных нюанса do shell script:
- Окружение урезанное. Это не твой интерактивный zsh - тут нет ~/.zshrc, PATH минимальный (/usr/bin:/bin:/usr/sbin:/sbin). Поэтому brew или другие команды из /opt/homebrew не найдутся: пиши полный путь /opt/homebrew/bin/brew.
- Привилегии. do shell script "..." with administrator privileges поднимет графический запрос пароля и выполнит команду под root. Удобно для разовых вещей, но пароль в скрипте открытым текстом - это дыра, для фоновых задач используй launchd с нужными правами, а не зашитый пароль.
Команды (Shortcuts) - это то, куда Apple вкладывается сейчас. Визуальный конструктор: собираешь цепочку действий блоками, без кода. Главный плюс перед AppleScript - кросс-Apple: один и тот же shortcut работает на Mac, iPhone, iPad и синхронизируется через iCloud. Действия покрывают файлы, приложения, систему, сеть, текст, и - что важно для нас - есть действие "Запустить скрипт" (shell, AppleScript, JXA) прямо внутри shortcut. То есть Shortcuts не заменяет AppleScript, а оборачивает его в удобный, синхронизируемый, запускаемый отовсюду контейнер.
И самое вкусное для сисадмина: с macOS есть CLI-утилита shortcuts. Это превращает визуальные сценарии в обычные команды для launchd, Makefile или CI.
Код: Выделить всё
# список всех команд
$ shortcuts list
Combine Images
Resize to 1080
Daily Report
# запуск по имени (пробелы - в кавычках или с экранированием)
$ shortcuts run "Combine Images"
# подать файлы на вход и забрать результат
$ shortcuts run "Resize to 1080" -i ~/Desktop/raw.png -o ~/Desktop/out.png
# пачка по маске на вход
$ shortcuts run "Combine Images" -i ~/Desktop/*.jpg
Когда Shortcuts проигрывает. Отладка скудная - нет нормального дебага, ошибка часто выглядит как "что-то пошло не так". Версионирование плохое: shortcut - это бинарный подписанный пакет, в git его не положишь по-человечески. И запуск по SSH без графической сессии часто спотыкается. Поэтому для серверных и headless-сценариев чистый AppleScript/osascript или shell остаются надежнее.
Automator: легаси, которое еще кормит, и Hammerspoon для тяжелого
Automator жив, но заморожен. Apple его не развивает: библиотека действий не растет, интерфейс не меняется с незапамятных времен, новых типов воркфлоу не появляется. Формально не deprecated, старые воркфлоу работают, но строить новое на нем - значит вкладываться в тупиковую ветку. Один кейс, где он еще полезен прямо сейчас:
- Быстрые действия (Quick Actions) - пункты в контекстном меню Finder и в Сервисах. Хотя то же самое уже умеет Shortcuts, и Apple туда мигрирует.
- Действия папки (Folder Actions) - триггер на появление файла в папке. Это нативный аналог inotify/watch, без сторонних утилит.
Когда же ни Shortcuts, ни AppleScript не хватает - заходит Hammerspoon. Это десктоп-автоматизация на Lua: мост в низкоуровневые API macOS - окна, экраны, мышь, клавиатура, системные события. Ставится через brew install hammerspoon --cask, конфиг живет в ~/.hammerspoon/init.lua. Его убойная фича - оконный менеджмент и глобальные хоткеи, чего стоковая система толком не дает.
Код: Выделить всё
-- ~/.hammerspoon/init.lua : Cmd+Alt+Left двигает окно в левую половину
hs.hotkey.bind({"cmd", "alt"}, "Left", function()
local win = hs.window.focusedWindow()
local f = win:screen():frame()
win:setFrame({x=f.x, y=f.y, w=f.w/2, h=f.h})
end)
Типичные грабли
- Ошибка -1743 и запросы автоматизации. Скрипт, работавший локально, по SSH или из launchd-демона падает: нет графической сессии для запроса разрешения TCC. Решение - выдать доступ заранее в Системных настройках, либо разворачивать PPPC-профиль через MDM.
- PATH в do shell script. Не находит brew/python - потому что окружение урезано. Всегда полный путь: /opt/homebrew/bin/... на Apple Silicon, /usr/local/bin/... на старых Intel.
- Кавычки слоями. osascript -e 'do shell script "..."' - три уровня экранирования. Не мучайся: выноси shell в отдельный файл.
- shortcuts run открывает GUI. Ждешь тихого фонового выполнения, а вылезает приложение. Помни: чисто headless из подкоманд только list; run на маке поднимает интерфейс и требует залогиненной сессии.
- Парсинг результата AppleScript. Список из AppleScript печатается как "a, b, c" - это не CSV и не JSON. Для машинной обработки бери JXA и возвращай JSON.stringify(...), парси через jq.
- Названия приложений. "tell application" хочет точное имя бандла как в /Applications. System Settings в коде по-прежнему откликается на старое имя в ряде скриптов - сверяйся со словарем приложения.
- Запусти osascript -e 'display notification "тест" with title "lab"' - убедись, что баннер прилетел.
- Сравни два мира: osascript -e 'tell application "Finder" to get name of home' и osascript -l JavaScript -e 'Application("Finder").home.name()'. Прими запрос автоматизации, посмотри одинаковый ли результат.
- Через do shell script вытащи системные данные: osascript -e 'do shell script "sw_vers -productVersion"'. Должна вернуться 26.x (Tahoe).
- Сломай и почини PATH: osascript -e 'do shell script "brew --version"' (упадет), потом замени на полный путь /opt/homebrew/bin/brew --version.
- Открой приложение Команды, собери shortcut из одного действия "Получить текущую дату" -> "Показать уведомление". Запусти его из терминала: shortcuts list, затем shortcuts run "имя".
- Положи JXA-скрипт с шебангом #!/usr/bin/osascript -l JavaScript в файл, chmod +x, запусти как ./script.
- Что такое Apple Events и при чем тут TCC, когда твой скрипт первый раз обращается к Finder?
- Чем shortcuts run принципиально отличается по применимости от osascript при запуске по SSH без графической сессии?
- Почему do shell script "brew install ..." падает с "command not found", и как это чинится одной правкой?
- В каком случае ты возьмешь Hammerspoon, а не Shortcuts или AppleScript?
Под капотом всей автоматизации mac лежат Apple Events и OSA - на них стоят AppleScript (классика, надежно, тонна примеров) и JXA (JavaScript, удобный для данных). osascript - твой вход из терминала, do shell script - выход в shell, и оба моста двусторонние. Shortcuts - современный, кросс-Apple, синхронизируемый слой, который оборачивает все это и запускается из CLI через shortcuts run; именно сюда стоит вкладывать новое. Automator оставь легаси-воркфлоу, а Hammerspoon бери, когда нужен программируемый контроль над окнами и системными событиями. Выбор простой: склеить приложения - AppleScript/JXA, собрать переносимый сценарий с триггером из скрипта - Shortcuts, рулить рабочим столом - Hammerspoon.