Конфигурация macos бывает двух видов: повторяемая и неповторяемая. Неповторяемая живёт в твоей голове и в мышечной памяти. Повторяемая - это текст, который можно положить в git, прогнать на десяти машинах и получить одинаковый результат. Весь этот урок про то, как превратить настройки в код: где они физически лежат, как их читать и писать командой defaults write, как лезть в сложные структуры через PlistBuddy, и где проходит граница, за которой локальный скрипт уже не годится и нужен профиль конфигурации или MDM.
Где живут настройки: формат plist
Почти все настройки на mac хранятся в файлах property list, расширение .plist. Это структурированный формат: словари (dict, ключ-значение), массивы (array), строки, числа, булевы, даты, бинарные данные. По сути - JSON до того, как JSON стал модным. Лежат plist в трёх основных местах, и это надо держать в голове:
- ~/Library/Preferences/ - настройки текущего пользователя. Здесь файлы вида com.apple.dock.plist, com.apple.finder.plist.
- /Library/Preferences/ - настройки уровня всей системы (нужен root для записи).
- ~/Library/Preferences/ByHost/ - настройки, привязанные к конкретному железу. К имени файла добавлен Hardware UUID машины.
Посмотрим, бинарный файл или XML, и переведём его в читаемый вид утилитой plutil:
Код: Выделить всё
$ file ~/Library/Preferences/com.apple.dock.plist
/Users/admin/Library/Preferences/com.apple.dock.plist: Apple binary property list
$ plutil -convert xml1 -o - ~/Library/Preferences/com.apple.dock.plist | head
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" ...>
<plist version="1.0">
<dict>
<key>autohide</key>
<true/>
<key>tilesize</key>
<integer>48</integer>
</dict>
</plist>
Код: Выделить всё
plutil -convert xml1 file.plistКод: Выделить всё
$ plutil -lint ~/Library/Preferences/com.apple.finder.plist
/Users/admin/Library/Preferences/com.apple.finder.plist: OK

defaults: рабочая лошадка
Команда defaults write - это твой основной инструмент. Синтаксис:
Код: Выделить всё
defaults write <домен> <ключ> -<тип> <значение>- -bool true/false - булево (autohide Dock, например).
- -int - целое (размер иконок).
- -float - дробное.
- -string - строка.
- -array - массив значений.
- -dict - словарь (пары ключ-значение).
Код: Выделить всё
$ defaults read com.apple.dock autohide
0
$ defaults write com.apple.dock autohide -bool true
$ defaults read com.apple.dock autohide
1
$ killall Dock
Есть глобальный домен - настройки, общие для всех приложений. Обращение к нему через -g (или NSGlobalDomain, это синонимы):
Код: Выделить всё
$ defaults write -g AppleShowAllExtensions -bool true
$ defaults write NSGlobalDomain KeyRepeat -int 2
Полезные флаги для отладки и инвентаризации:
- defaults domains - список всех доменов пользователя через запятую.
- defaults read com.apple.finder - целиком дамп домена (увидишь всю структуру).
- defaults delete com.apple.dock autohide - снести ключ (вернуть к умолчанию).
- defaults read-type com.apple.dock tilesize - узнать тип хранимого значения.
Код: Выделить всё
$ defaults -currentHost write com.apple.screensaver idleTime -int 0
$ ls ~/Library/Preferences/ByHost/ | grep screensaver
com.apple.screensaver.A1B2C3D4-....plist
PlistBuddy: когда defaults не хватает
defaults отлично пишет плоские значения, но плохо работает с вложенностью - массив словарей, словарь внутри массива внутри словаря. Тут выходит на сцену /usr/libexec/PlistBuddy. Он не в PATH, поэтому всегда полным путём. В скриптах работаем неинтерактивно через -c (одна команда на флаг, можно несколько -c подряд).
Допустим, надо собрать plist с массивом словарей (типичная структура для списка элементов Dock или подобного):
Код: Выделить всё
$ PB=/usr/libexec/PlistBuddy
$ FILE=~/Desktop/demo.plist
$ $PB -c "Add :Apps array" "$FILE"
$ $PB -c "Add :Apps:0 dict" "$FILE"
$ $PB -c "Add :Apps:0:Name string Safari" "$FILE"
$ $PB -c "Add :Apps:0:Pinned bool true" "$FILE"
$ $PB -c "Print" "$FILE"
Dict {
Apps = Array {
Dict {
Name = Safari
Pinned = true
}
}
}
Скрипт автонастройки нового Mac
Собираем всё в один идемпотентный скрипт. Идемпотентный значит: запусти хоть десять раз - результат один и тот же, повторный прогон не ломает. defaults write для этого идеален, он перезаписывает значение, а не накапливает.
Код: Выделить всё
#!/bin/zsh
# setup-mac.sh - базовая настройка нового Mac
set -euo pipefail
echo "Finder: показывать расширения и спрятанные файлы"
defaults write NSGlobalDomain AppleShowAllExtensions -bool true
defaults write com.apple.finder AppleShowAllFiles -bool true
defaults write com.apple.finder ShowPathbar -bool true
defaults write com.apple.finder FXDefaultSearchScope -string "SCcf"
echo "Dock: автоскрытие, мелкие иконки, без недавних"
defaults write com.apple.dock autohide -bool true
defaults write com.apple.dock tilesize -int 42
defaults write com.apple.dock show-recents -bool false
echo "Скриншоты: PNG в ~/Screenshots без тени"
mkdir -p "$HOME/Screenshots"
defaults write com.apple.screencapture location -string "$HOME/Screenshots"
defaults write com.apple.screencapture type -string "png"
defaults write com.apple.screencapture disable-shadow -bool true
echo "Не плодить .DS_Store на сетевых и USB томах"
defaults write com.apple.desktopservices DSDontWriteNetworkStores -bool true
defaults write com.apple.desktopservices DSDontWriteUSBStores -bool true
echo "Применяем"
killall Dock Finder SystemUIServer 2>/dev/null || true
echo "Готово. Часть настроек подхватится после релогина."
Профили конфигурации: декларативная альтернатива
Скрипт defaults - это императив: пользователь может тут же зайти в настройки и всё отменить. Для своей машины нормально. Для парка корпоративных Mac, где политику безопасности нельзя оставлять на совесть юзера, нужен другой механизм - профиль конфигурации, файл .mobileconfig.
Профиль - это XML property list (тот же plist, только специальной структуры): верхний словарь с метаданными плюс массив payload, где каждый payload настраивает свою подсистему - пароли, Wi-Fi, ограничения, доменные настройки приложений. Принципиальное отличие от defaults: профиль накладывает настройки сверху и блокирует их. Это managed-значение. Пользователь физически не может изменить параметр, заданный профилем, - в Системных настройках пункт станет серым с пометкой, что им управляет профиль. defaults read такое значение даже не покажет в обычном домене, потому что managed preferences живут отдельно. Снять профиль может только тот, кто его поставил (или MDM).
Скелет .mobileconfig, который форсит autohide Dock как managed:
Код: Выделить всё
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" ...>
<plist version="1.0">
<dict>
<key>PayloadContent</key>
<array>
<dict>
<key>PayloadType</key>
<string>com.apple.dock</string>
<key>PayloadIdentifier</key>
<string>ru.cyberlake.dock</string>
<key>PayloadUUID</key>
<string>11111111-1111-1111-1111-111111111111</string>
<key>autohide</key>
<true/>
</dict>
</array>
<key>PayloadType</key>
<string>Configuration</string>
<key>PayloadIdentifier</key>
<string>ru.cyberlake.profile</string>
<key>PayloadUUID</key>
<string>22222222-2222-2222-2222-222222222222</string>
<key>PayloadDisplayName</key>
<string>Cyberlake Dock policy</string>
</dict>
</plist>
Код: Выделить всё
uuidgenMDM против локального скрипта: где граница
Дважды кликнуть по .mobileconfig можно вручную - откроется установка профиля в Системных настройках. Но руками это не масштабируется, и в 2026 это уже не основной путь. Профили раздаёт MDM (Mobile Device Management) - сервер управления. И тут важный сдвиг эпохи: 2026 - год, когда Apple делает Declarative Device Management (DDM) стандартом. Разница в подходе:
Как выбирать инструмент под задачу:Старый MDM - это команды: сервер шлёт устройству "сделай вот это". DDM - это декларации: сервер описывает желаемое состояние, а устройство само непрерывно сверяет свою конфигурацию с эталоном и само себя чинит при дрейфе, отчитываясь асинхронно. Никакого постоянного опроса сервера - меньше нагрузки на сеть в большом парке.
- Свой Mac, dotfiles, разработчик - скрипт defaults/PlistBuddy в git. Гибко, прозрачно, версионируется.
- Нужно запретить пользователю менять настройку - только профиль конфигурации. Скрипт тут бессилен, юзер всё откатит.
- Парк машин, корпоратив, удалённый контроль и compliance - MDM, и в 2026 на базе DDM, где устройства держат состояние сами.
Типичные грабли
- Забыл killall - написал в plist, на экране ничего. Процесс не перечитал настройки. После Dock - killall Dock, после Finder - killall Finder.
- Не указал тип - defaults write ... true без -bool запишет строку "true", а не булево. Приложение проигнорирует. Всегда явный -bool/-int/-string.
- Правка plist текстовым редактором - cfprefsd закеширует и затрёт. Пиши через defaults, не открывай файл в Vim.
- ByHost-файл скопировали на другую машину - не сработает, у неё другой Hardware UUID. -currentHost прописывай на каждой машине отдельно.
- Битый .mobileconfig - незакрытый тег, установка молча падает. plutil -lint перед раздачей, всегда.
- Ждёшь, что профиль изменит обычный домен - managed-значения живут отдельно, defaults read их в обычном домене не покажет. Профиль накладывает сверху, не правит пользовательский plist.
Повтори руками, на своей машине, безопасно:
- Прочитай текущее autohide Dock: . Запомни значение.
Код: Выделить всё
defaults read com.apple.dock autohide - Включи автоскрытие: . Посмотри на Dock.
Код: Выделить всё
defaults write com.apple.dock autohide -bool true && killall Dock - Найди файл домена и проверь формат: , затем
Код: Выделить всё
file ~/Library/Preferences/com.apple.dock.plist.Код: Выделить всё
plutil -convert xml1 -o - ~/Library/Preferences/com.apple.dock.plist | head -20 - Создай свой plist через PlistBuddy с массивом словарей (как в примере выше) и выведи через Print. Проверь .
Код: Выделить всё
plutil -lint ~/Desktop/demo.plist - Верни Dock как было: , либо
Код: Выделить всё
defaults write com.apple.dock autohide -bool <твоё_исходное> && killall Dock.Код: Выделить всё
defaults delete com.apple.dock autohide - Собери из трёх-четырёх своих defaults write мини-скрипт setup.sh с set -euo pipefail и killall в конце. Запусти дважды - убедись, что повторный прогон ничего не ломает (идемпотентность).
- Чем отличается запись через defaults write от прямого редактирования plist в текстовом редакторе, и почему второе опасно?
- Зачем после defaults write обычно нужен killall, и для каких процессов?
- В каком случае defaults бессилен и нужен именно профиль конфигурации .mobileconfig?
- Что делает флаг -currentHost, где оказывается файл, и почему такой plist нельзя просто скопировать на другую машину?
Настройки macos - это структурированные plist в трёх местах (~/Library, /Library, ByHost), читаемые plutil и правимые через defaults и PlistBuddy, но не текстовым редактором. defaults write с явным типом плюс killall - твой повседневный инструмент и основа идемпотентного скрипта автонастройки в git. PlistBuddy подключаешь, когда нужна вложенность. А когда настройку надо именно запретить пользователю или раскатать на парк - это уже профиль .mobileconfig и MDM, в 2026 - на декларативной модели DDM, где устройства держат состояние сами. Скрипт - для своего и для удобства, профиль - для безопасности и политик. Вот и вся конфигурация как код на Mac.