CLI на macOS: BSD-команды и отличия от Linux

Рейтинг: 70.2% · 15 голосов
Подробный курс по macOS для разработчиков и админов: архитектура и Apple Silicon, терминал и zsh, Homebrew и пакеты, launchd и автоматизация, APFS и Time Machine, безопасность (SIP, Gatekeeper, TCC, FileVault), сеть, логи и диагностика, MDM и DDM, контейнеры и виртуализация, восстановление. По официальной документации Apple, актуально на 2026 (macOS 26 Tahoe).
Ответить
Аватара пользователя
Artem_MacAdmin
Сообщения: 47
Зарегистрирован: 11 май 2026, 05:31

CLI на macOS: BSD-команды и отличия от Linux

Сообщение Artem_MacAdmin »

Оглавление курса (47)
  1. Что такое macOS: Unix под капотом и место в 2026
  2. Архитектура macOS: XNU, тома и Signed System Volume
  3. Apple Silicon: ARM, Rosetta 2 и отличия от Intel
  4. Установка, обновление и восстановление macOS
  5. Finder, рабочий стол и продуктивность
  6. Системные настройки и их управление из CLI (defaults)
  7. Пользователи и группы: учётные записи и управление
  8. Терминал и zsh: командная строка macOS
  9. CLI на macOS: BSD-команды и отличия от Linux (вы здесь)
  10. Настройка zsh: .zshrc, prompt, плагины
  11. Файловая система и права: POSIX, ACL, атрибуты, флаги
  12. Homebrew: пакетный менеджер для macOS
  13. Homebrew глубже: tap, Brewfile, services, обслуживание
  14. Другие пакеты: MacPorts, mas, Nix и App Store
  15. Установка приложений: .app, .dmg, .pkg и Gatekeeper
  16. Управление приложениями и их данные
  17. launchd и launchctl: система запуска macOS
  18. Свои службы и периодические задачи на launchd
  19. Автоматизация: Shortcuts, AppleScript и osascript
  20. Конфигурация как код: defaults, plist и профили
  21. APFS: снапшоты, клоны, контейнеры и шифрование
  22. Управление дисками: diskutil, образы и форматирование
  23. Time Machine и резервное копирование
  24. Внешние диски, форматы и сетевые тома
  25. Модель безопасности macOS: SIP и целостность системы
  26. Gatekeeper, нотаризация и защита от вредоносов
  27. Приватность и TCC: разрешения на доступ
  28. FileVault: полнодисковое шифрование
  29. Связка ключей и управление секретами
  30. Сетевой экран и защита сети
  31. Сеть в macOS: настройка и инструменты
  32. Диагностика сети на Mac
  33. Общий доступ: SSH, экран, файлы, удалённое управление
  34. Процессы и ресурсы: мониторинг производительности
  35. Логи macOS: unified logging и диагностика
  36. Производительность и энергия: powermetrics, диагностика
  37. Энергия, сон и обслуживание системы
  38. Профили конфигурации и основы MDM
  39. Управление парком Mac: MDM, DDM и zero-touch (2026)
  40. Среда разработчика на Mac: Xcode CLT, git, версии
  41. Контейнеры на Mac: Docker Desktop, OrbStack, Apple container
  42. Виртуализация: Virtualization.framework, UTM, Linux и Windows
  43. Режимы загрузки и восстановление (Apple Silicon)
  44. Сброс, NVRAM, Safe Mode и сброс настроек
  45. Траблшутинг macOS: дерево решений по симптомам
  46. Место на диске и обслуживание системы
  47. Сквозной проект: настройка Mac как у профи + куда расти
Ты пришёл на Mac с Linux, открыл терминал, набрал привычное

Код: Выделить всё

sed -i 's/foo/bar/' file
и получил в ответ либо ошибку, либо файл с расширением вместо отредактированного содержимого. Или скопировал из чужого скрипта

Код: Выделить всё

find . -printf '%P\n'
, а оно где-то работает, где-то ругается. Или дёрнул

Код: Выделить всё

ldd ./binary
- команды нет вообще. Знакомо? Дело не в том, что macOS сломан. Дело в том, что командная строка macOS - это BSD, а не GNU/Linux. И пока ты этого не уложил в голове, любая команда терминала mac - это лотерея.

Этот урок - про то, почему так, как именно отличается поведение базовых утилит, и как привести инструментарий в чувство, чтобы твои bsd команды mac и привычные linux-команды перестали воевать. Разберём механику, реальный вывод, грабли и поставим GNU coreutils mac рядом с родными утилитами.

Откуда ноги растут: Darwin, BSD и почему это не Linux

macOS построен на ядре XNU и пользовательском окружении, унаследованном в основном от FreeBSD (плюс куски NeXTSTEP и собственный код Apple). Проверь сам:

Код: Выделить всё

$ uname -a
Darwin MacBook-Pro.local 25.5.0 Darwin Kernel Version 25.5.0: ... arm64

$ sw_vers
ProductName:    macOS
ProductVersion: 26.5.1
BuildVersion:   25F80
Тут уже видно две вещи. Первая - uname докладывает Darwin и версию ядра (25.x), а не версию macOS. Версию системы отдаёт отдельная утилита sw_vers - это уже mac-специфика, на Linux её нет. Вторая - архитектура arm64: на Apple Silicon (M-серия) это норма, на старых Intel-маках будет x86_64. macOS 26 Tahoe - последняя версия с поддержкой Intel, дальше только Apple Silicon, так что arm64 ты будешь видеть всё чаще.

Практический вывод: утилиты в

Код: Выделить всё

/usr/bin
и - это BSD-реализации. У них своя родословная, свои флаги и свой man. GNU-шный grep, sed, find, ls, stat, date - это другие программы с теми же именами. Совпадает имя и базовый POSIX-функционал, расходится всё, что сверху. А сверху - как раз самое вкусное, что ты привык использовать в скриптах.

Изображение

Где именно расходится поведение: разбор по утилитам

sed -i - главная мина. В GNU sed флаг (in-place) можно писать без аргумента, а суффикс бэкапа клеить вплотную:

Код: Выделить всё

sed -i.bak
или просто . В BSD sed обязательно требует аргумент - строку суффикса бэкапа. Хочешь без бэкапа - передай пустую строку отдельным аргументом:

Код: Выделить всё

# BSD (macOS) - правильно, без бэкапа:
sed -i '' 's/foo/bar/' file.txt

# с бэкапом file.txt.bak:
sed -i '.bak' 's/foo/bar/' file.txt

# а вот это (привычка из Linux) на macOS сломается:
sed -i 's/foo/bar/' file.txt
Что произойдёт в последнем случае: BSD sed съест

Код: Выделить всё

's/foo/bar/'
как суффикс бэкапа, а следующий аргумент

Код: Выделить всё

file.txt
примет за выражение - и упадёт с

Код: Выделить всё

undefined label
или сделает не то. Классика, на которой горят все мигранты. Проверка вживую:

Код: Выделить всё

$ printf 'foo\n' > t.txt
$ sed -i '' 's/foo/bar/' t.txt && cat t.txt
bar
Второе по частоте - escape-последовательности. GNU sed понимает , , , в выражениях. BSD sed - нет (или трактует иначе). в BSD sed внутри замены может вставить буквальную букву t, а не табуляцию. Лечится либо реальным символом (нажми Ctrl-V Tab), либо синтаксисом в zsh, либо переходом на awk/perl, либо установкой GNU sed (об этом ниже).

find - флаги и -printf. Исторически у BSD find не было (это GNU-расширение), и переносить linux-скрипты с

Код: Выделить всё

find ... -printf
было больно - приходилось городить

Код: Выделить всё

-exec stat
. Но осторожно: на свежих macOS (Tahoe, 26.x) find подтянул из современного FreeBSD, и теперь работает:

Код: Выделить всё

$ find /etc/hosts -maxdepth 0 -printf '%p\n'
/etc/hosts
Не радуйся раньше времени: на машинах с macOS 13-14 этого ещё нет, и набор спецификаторов у BSD-версии уже, чем у GNU. Если пишешь скрипт под парк машин - не закладывайся на , держи fallback. Ещё одно отличие: порядок аргументов в BSD find строже, а , , типы частично различаются. Например, GNU-шный

Код: Выделить всё

find . -regextype posix-extended
на BSD не существует - там флаг перед путями:

Код: Выделить всё

find -E . -regex '.*\.(jpg|png)'
.

ls - цвета и формат времени. GNU ls раскрашивает вывод по

Код: Выделить всё

--color=auto
. BSD ls делает то же через и переменную

Код: Выделить всё

LSCOLORS
(не

Код: Выделить всё

LS_COLORS
, это разные форматы). Длинных опций ,

Код: Выделить всё

--group-directories-first
у BSD ls нет. Сортировка по времени, человекочитаемые размеры совпадают, а вот

Код: Выделить всё

ls --time-style
- чисто GNU.

stat - полностью разный синтаксис. Тут даже не отличие, а другой язык. GNU:

Код: Выделить всё

stat -c '%s %n'
. BSD:

Код: Выделить всё

stat -f '%z %N'
. Спецификаторы не совпадают ни буквами, ни смыслом. Пример BSD:

Код: Выделить всё

$ stat -f '%Sp %Su %z %Sm' /etc/hosts
-rw-r--r-- root 320 Jun  9 21:15:45 2026
Здесь - права в виде строки, - владелец, - размер, - время модификации. Скрипт со просто не запустится.

date - арифметика и эпоха. GNU:

Код: Выделить всё

date -d '@0'
или

Код: Выделить всё

date -d 'yesterday'
. BSD:

Код: Выделить всё

date -r 0
переводит unix-время в дату, а сдвиги делаются флагом :

Код: Выделить всё

$ date -r 0
Thu Jan  1 03:00:00 MSK 1970

$ date -v-1d +%Y-%m-%d      # вчера
2026-06-14
- минус один день, - плюс месяц, - минус час. Логично и удобно, но синтаксис

Код: Выделить всё

-d 'yesterday'
из linux-скриптов не сработает.

grep. Тут ближе всего: BSD grep понимает , , , , (Perl-regex) на свежих версиях есть. Но исторически хромал, и тяжёлые PCRE-выражения лучше гонять через GNU grep или ripgrep.

Нет ldd. На Linux зависимости динамической библиотеки смотрят через . В macOS его нет в принципе - другой загрузчик (dyld) и формат Mach-O вместо ELF. Аналог - otool:

Код: Выделить всё

$ otool -L /bin/ls
/bin/ls:
    /usr/lib/libutil.dylib (compatibility version 1.0.0, ...)
    /usr/lib/libncurses.5.4.dylib (...)
    /usr/lib/libSystem.B.dylib (...)

Код: Выделить всё

otool -L
- это твой ldd на маке. А вместо strace для трассировки системных вызовов - dtruss (обёртка над DTrace, требует sudo и часто отключения SIP для системных бинарей). Это уже тяжёлая артиллерия, но знать про замену надо: ldd -> otool -L, strace -> dtruss, ltrace -> dtrace.

Лечим разнобой: GNU coreutils mac через Homebrew

Если ты постоянно живёшь в скриптах, написанных под Linux, проще не переучиваться, а поставить GNU-утилиты рядом. Делается это через Homebrew (на Apple Silicon он лежит в

Код: Выделить всё

/opt/homebrew
, на Intel - в

Код: Выделить всё

/usr/local
):

Код: Выделить всё

brew install coreutils findutils gnu-sed gawk grep gnu-tar
Ключевой момент - конфликт имён. GNU-утилиты называются так же, как BSD-родные (ls, sed, find). Чтобы не затирать системные (а затирать их в

Код: Выделить всё

/usr/bin
и нельзя, и опасно), Homebrew по умолчанию ставит их с префиксом g:

Код: Выделить всё

$ brew install gnu-sed
$ which gsed
/opt/homebrew/bin/gsed

$ gsed -i 's/foo/bar/' file.txt   # работает как в Linux, без пустой ''
Так появляются gsed, gfind, gls, gstat, gdate, gawk, ggrep, gtar и десятки утилит из coreutils (, , , , и т.д.). Команды терминала mac начинают вести себя ровно как на linux-сервере - но только когда ты явно зовёшь g-версию.

Если хочешь, чтобы без префикса означал GNU sed (например, общий скрипт гоняется и на маке, и на linux), Homebrew подсказывает путь к каталогу , где утилиты лежат под обычными именами:

Код: Выделить всё

# для coreutils:
export PATH="/opt/homebrew/opt/coreutils/libexec/gnubin:$PATH"
# для gnu-sed:
export PATH="/opt/homebrew/opt/gnu-sed/libexec/gnubin:$PATH"
Добавь эти строки в

Код: Выделить всё

~/.zshrc
(zsh - оболочка по умолчанию с macOS Catalina). После

Код: Выделить всё

source ~/.zshrc
команда будет ссылаться на GNU sed. Осторожно: это палка о двух концах. Часть системных скриптов и сторонних установщиков рассчитывает на BSD-поведение

Код: Выделить всё

/usr/bin
. Если глобально подменишь PATH, можешь словить тонкие баги в чужих скриптах. Профессиональный компромисс - не подменять глобально, а звать g-версии явно (gsed, gfind), и только в своём окружении при необходимости включать gnubin.

Полезные mac-команды, которых нет на Linux

Отличия - это не только боль. У macOS есть набор утилит, ради которых стоит лезть в терминал mac команды:
  • open - открыть файл/папку/URL дефолтным приложением. откроет текущую папку в Finder,

    Код: Выделить всё

    open -a Safari https://cyberlake.ru
    - в конкретном приложении,

    Код: Выделить всё

    open -e file.txt
    - в TextEdit. Аналог

    Код: Выделить всё

    xdg-open
    , но удобнее.
  • pbcopy / pbpaste - буфер обмена из CLI.

    Код: Выделить всё

    cat key.pub | pbcopy
    - и ключ уже в clipboard.

    Код: Выделить всё

    pbpaste | grep error
    - грепнуть то, что скопировал мышкой. Незаменимо в скриптах.
  • mdfind - Spotlight из командной строки. Ищет по индексу, поэтому мгновенно, в отличие от рекурсивного find.

    Код: Выделить всё

    mdfind -name report.pdf
    ,

    Код: Выделить всё

    mdfind 'kMDItemContentType==public.image' -onlyin ~/Downloads
    . По содержимому:

    Код: Выделить всё

    mdfind "договор аренды"
    найдёт файлы, внутри которых есть эта фраза.

    Код: Выделить всё

    mdfind kMDItemAppStoreHasReceipt=1
    - все приложения, поставленные из App Store.
  • caffeinate - не дать маку уснуть.

    Код: Выделить всё

    caffeinate -i make build
    держит систему бодрой, пока идёт сборка, и отпускает по завершении.

    Код: Выделить всё

    caffeinate -d
    не гасит дисплей,

    Код: Выделить всё

    caffeinate -t 3600
    - на час.
  • say - синтез речи.

    Код: Выделить всё

    make && say "сборка готова" || say "упало"
    - звуковое уведомление о конце долгой задачи.
  • system_profiler - полный отчёт о железе и софте.

    Код: Выделить всё

    system_profiler SPHardwareDataType
    выдаст модель, чип, серийник, RAM;

    Код: Выделить всё

    SPSoftwareDataType
    - версию ОС и аптайм. Это машиночитаемый источник правды о машине, удобно для инвентаризации парка.

Код: Выделить всё

$ system_profiler SPHardwareDataType
Hardware:
      Model Name: MacBook Pro
      Chip: Apple M4 Pro
      Total Number of Cores: 14 (10 performance and 4 efficiency)
      Memory: 48 GB
      Serial Number: XXXXXXXXXX
Разбор:

Код: Выделить всё

SPHardwareDataType
- один из дата-типов профайлера (полный список -

Код: Выделить всё

system_profiler -listDataTypes
). Для скриптов добавь и парсь через . Для быстрой версии ОС хватает

Код: Выделить всё

sw_vers -productVersion
, для номера сборки -

Код: Выделить всё

-buildVersion
.

Где смотреть правду: man, which, type

Главное правило при сомнении - читай man именно на этой машине, а не гугли GNU-маны. на маке покажет BSD-секцию опций, и ты сразу увидишь, что требует аргумент. У многих BSD-утилит справка плотная и точная.

Чтобы понять, какую именно программу ты сейчас зовёшь - BSD из

Код: Выделить всё

/usr/bin
или GNU из

Код: Выделить всё

/opt/homebrew
- используй which и type:

Код: Выделить всё

$ which -a sed
/usr/bin/sed

$ type sed
sed is /usr/bin/sed

$ type ll
ll is an alias for ls -lG

Код: Выделить всё

which -a
покажет все совпадения в PATH по порядку - так ловят ситуацию, когда gnubin подменил утилиту. в zsh умнее: различает бинарь, alias, функцию и встроенную команду шелла. Если вдруг ведёт себя странно - первым делом

Код: Выделить всё

type sed
, и сразу видно, не подсунул ли кто GNU-версию или alias.

Типичные грабли
Мини-лаба (повтори руками)
  • Узнай версию системы и чип: и

    Код: Выделить всё

    system_profiler SPHardwareDataType
    .
  • Создай файл и отредактируй на месте по-BSD:

    Код: Выделить всё

    printf 'foo\n' > t.txt; sed -i '' 's/foo/bar/' t.txt; cat t.txt
    . Затем намеренно ошибись -

    Код: Выделить всё

    sed -i 's/bar/baz/' t.txt
    - и прочитай ошибку.
  • Посмотри метаданные через BSD stat:

    Код: Выделить всё

    stat -f '%Sp %Su %z %Sm' t.txt
    . Сравни спецификаторы с тем, что выдаёт

    Код: Выделить всё

    man stat
    .
  • Найди файл двумя способами: рекурсивно

    Код: Выделить всё

    find ~ -name t.txt 2>/dev/null
    и через Spotlight

    Код: Выделить всё

    mdfind -name t.txt
    . Заметь разницу в скорости.
  • Поставь GNU sed и сравни:

    Код: Выделить всё

    brew install gnu-sed; gsed -i 's/baz/foo/' t.txt; which -a sed
    . Убедись, что не требует пустой строки.
  • Глянь зависимости бинаря:

    Код: Выделить всё

    otool -L /bin/ls
    . Это твой ldd.
Контрольные вопросы
  • Почему

    Код: Выделить всё

    sed -i 's/a/b/' f
    ломается на macOS и как записать то же самое правильно?
  • Чем отличается от того, что нужно на маке, и какой флаг там вместо ?
  • Ты поставил coreutils. Как теперь вызвать GNU-версию ls и как проверить, какая утилита реально запускается под именем ?
  • Какой командой посмотреть зависимости динамической библиотеки, раз на маке нет?
Итог

Запомни одну рамку: терминал на macOS - это BSD, и базовые утилиты это другие программы с теми же именами, что на Linux. Совпадает POSIX-ядро, расходятся надстройки - , , , , . Ловушек немного, и все они укладываются в голове за день: пустая строка после , вместо у stat, и у date,

Код: Выделить всё

otool -L
вместо . Хочешь линуксового комфорта - ставь coreutils, gnu-sed и зови g-версии явно. А взамен получаешь open, pbcopy, mdfind, caffeinate и system_profiler, которых на linux нет. Сомневаешься в поведении - на этой машине и

Код: Выделить всё

type команда
, и ты всегда знаешь, что именно запускаешь.
👍4 ❤️ 🔥1 😄 🤔
Аватара пользователя
nikitatravkin
Сообщения: 1
Зарегистрирован: 24 май 2026, 05:31

Re: CLI на macOS: BSD-команды и отличия от Linux

Сообщение nikitatravkin »

Спасибо, наконец дошло почему мой sed -i всё время портил файлы на маке. Пустая кавычка реально спасла
👍 ❤️1 🔥 😄 🤔
Аватара пользователя
basedburnout
Сообщения: 1
Зарегистрирован: 14 май 2026, 02:49

Re: CLI на macOS: BSD-команды и отличия от Linux

Сообщение basedburnout »

А правда что на старых маках (на работе у нас 13.6) find -printf не пашет? Поставил findutils, через gfind всё ок, но хотелось бы без костылей
👍3 ❤️ 🔥1 😄 🤔
Ответить
← Предыдущая глава
Терминал и zsh: командная строка macOS
Следующая глава →
Настройка zsh: .zshrc, prompt, плагины

Все главы курса «macOS для профи: администрирование, терминал и Homebrew»

Поделиться темой: ✈ Telegram VK
Похожие запросы: Что такое macOS и Unix под капотомТерминал и команды macOS

Вернуться в «macOS для профи: администрирование, терминал и Homebrew»

Кто сейчас на конференции

Сейчас этот форум просматривают: нет зарегистрированных пользователей и 1 гость