Код: Выделить всё
sed -i 's/foo/bar/' fileКод: Выделить всё
find . -printf '%P\n'Код: Выделить всё
ldd ./binaryЭтот урок - про то, почему так, как именно отличается поведение базовых утилит, и как привести инструментарий в чувство, чтобы твои 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
Практический вывод: утилиты в
Код: Выделить всё
/usr/binКод: Выделить всё
/bin
Где именно расходится поведение: разбор по утилитам
sed -i - главная мина. В GNU sed флаг
Код: Выделить всё
-iКод: Выделить всё
sed -i.bakКод: Выделить всё
sed -iКод: Выделить всё
-iКод: Выделить всё
# 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
Код: Выделить всё
's/foo/bar/'Код: Выделить всё
file.txtКод: Выделить всё
undefined labelКод: Выделить всё
$ printf 'foo\n' > t.txt
$ sed -i '' 's/foo/bar/' t.txt && cat t.txt
bar
Код: Выделить всё
\tКод: Выделить всё
\nКод: Выделить всё
\sКод: Выделить всё
\+Код: Выделить всё
\tКод: Выделить всё
$'...'find - флаги и -printf. Исторически у BSD find не было
Код: Выделить всё
-printfКод: Выделить всё
find ... -printfКод: Выделить всё
-exec statКод: Выделить всё
-printfКод: Выделить всё
$ find /etc/hosts -maxdepth 0 -printf '%p\n'
/etc/hosts
Код: Выделить всё
-printfКод: Выделить всё
-deleteКод: Выделить всё
-regexКод: Выделить всё
-typeКод: Выделить всё
find . -regextype posix-extendedКод: Выделить всё
-EКод: Выделить всё
find -E . -regex '.*\.(jpg|png)'ls - цвета и формат времени. GNU ls раскрашивает вывод по
Код: Выделить всё
--color=autoКод: Выделить всё
-GКод: Выделить всё
LSCOLORSКод: Выделить всё
LS_COLORSКод: Выделить всё
--colorКод: Выделить всё
--group-directories-firstКод: Выделить всё
-hКод: Выделить всё
ls --time-stylestat - полностью разный синтаксис. Тут даже не отличие, а другой язык. GNU:
Код: Выделить всё
stat -c '%s %n'Код: Выделить всё
stat -f '%z %N'Код: Выделить всё
$ stat -f '%Sp %Su %z %Sm' /etc/hosts
-rw-r--r-- root 320 Jun 9 21:15:45 2026
Код: Выделить всё
%SpКод: Выделить всё
%SuКод: Выделить всё
%zКод: Выделить всё
%SmКод: Выделить всё
stat -cdate - арифметика и эпоха. GNU:
Код: Выделить всё
date -d '@0'Код: Выделить всё
date -d 'yesterday'Код: Выделить всё
date -r 0Код: Выделить всё
-vКод: Выделить всё
$ date -r 0
Thu Jan 1 03:00:00 MSK 1970
$ date -v-1d +%Y-%m-%d # вчера
2026-06-14
Код: Выделить всё
-v-1dКод: Выделить всё
-v+1mКод: Выделить всё
-v-1HКод: Выделить всё
-d 'yesterday'grep. Тут ближе всего: BSD grep понимает
Код: Выделить всё
-EКод: Выделить всё
-iКод: Выделить всё
-rКод: Выделить всё
-oКод: Выделить всё
-PКод: Выделить всё
-PНет ldd. На Linux зависимости динамической библиотеки смотрят через
Код: Выделить всё
lddКод: Выделить всё
$ 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Лечим разнобой: GNU coreutils mac через Homebrew
Если ты постоянно живёшь в скриптах, написанных под Linux, проще не переучиваться, а поставить GNU-утилиты рядом. Делается это через Homebrew (на Apple Silicon он лежит в
Код: Выделить всё
/opt/homebrewКод: Выделить всё
/usr/localКод: Выделить всё
brew install coreutils findutils gnu-sed gawk grep gnu-tar
Код: Выделить всё
/usr/binКод: Выделить всё
$ brew install gnu-sed
$ which gsed
/opt/homebrew/bin/gsed
$ gsed -i 's/foo/bar/' file.txt # работает как в Linux, без пустой ''
Код: Выделить всё
gcpКод: Выделить всё
gmvКод: Выделить всё
grmКод: Выделить всё
gsortКод: Выделить всё
gduЕсли хочешь, чтобы
Код: Выделить всё
sedКод: Выделить всё
gnubinКод: Выделить всё
# для coreutils:
export PATH="/opt/homebrew/opt/coreutils/libexec/gnubin:$PATH"
# для gnu-sed:
export PATH="/opt/homebrew/opt/gnu-sed/libexec/gnubin:$PATH"
Код: Выделить всё
~/.zshrcКод: Выделить всё
source ~/.zshrcКод: Выделить всё
sedКод: Выделить всё
/usr/binПолезные mac-команды, которых нет на Linux
Отличия - это не только боль. У macOS есть набор утилит, ради которых стоит лезть в терминал mac команды:
- open - открыть файл/папку/URL дефолтным приложением. откроет текущую папку в Finder,
Код: Выделить всё
open .- в конкретном приложении,Код: Выделить всё
open -a Safari https://cyberlake.ru- в TextEdit. АналогКод: Выделить всё
open -e file.txt, но удобнее.Код: Выделить всё
xdg-open - pbcopy / pbpaste - буфер обмена из CLI. - и ключ уже в clipboard.
Код: Выделить всё
cat key.pub | pbcopy- грепнуть то, что скопировал мышкой. Незаменимо в скриптах.Код: Выделить всё
pbpaste | grep error - mdfind - Spotlight из командной строки. Ищет по индексу, поэтому мгновенно, в отличие от рекурсивного find. ,
Код: Выделить всё
mdfind -name report.pdf. По содержимому:Код: Выделить всё
mdfind 'kMDItemContentType==public.image' -onlyin ~/Downloadsнайдёт файлы, внутри которых есть эта фраза.Код: Выделить всё
mdfind "договор аренды"- все приложения, поставленные из App Store.Код: Выделить всё
mdfind kMDItemAppStoreHasReceipt=1 - caffeinate - не дать маку уснуть. держит систему бодрой, пока идёт сборка, и отпускает по завершении.
Код: Выделить всё
caffeinate -i make buildне гасит дисплей,Код: Выделить всё
caffeinate -d- на час.Код: Выделить всё
caffeinate -t 3600 - say - синтез речи. - звуковое уведомление о конце долгой задачи.
Код: Выделить всё
make && say "сборка готова" || say "упало" - system_profiler - полный отчёт о железе и софте. выдаст модель, чип, серийник, RAM;
Код: Выделить всё
system_profiler SPHardwareDataType- версию ОС и аптайм. Это машиночитаемый источник правды о машине, удобно для инвентаризации парка.Код: Выделить всё
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Код: Выделить всё
-jsonКод: Выделить всё
jqКод: Выделить всё
sw_vers -productVersionКод: Выделить всё
-buildVersionГде смотреть правду: man, which, type
Главное правило при сомнении - читай man именно на этой машине, а не гугли GNU-маны.
Код: Выделить всё
man sedКод: Выделить всё
-iЧтобы понять, какую именно программу ты сейчас зовёшь - BSD из
Код: Выделить всё
/usr/binКод: Выделить всё
/opt/homebrewКод: Выделить всё
$ which -a sed
/usr/bin/sed
$ type sed
sed is /usr/bin/sed
$ type ll
ll is an alias for ls -lG
Код: Выделить всё
which -aКод: Выделить всё
typeКод: Выделить всё
sedКод: Выделить всё
type sedТипичные грабли
- sed -i без пустой строки - правит не тот файл или падает. Всегда на BSD.
Код: Выделить всё
sed -i '' - Копипаст linux-скрипта целиком. ,
Код: Выделить всё
stat -c,Код: Выделить всё
date -d(на старых macOS),Код: Выделить всё
find -printf,Код: Выделить всё
readlink -f- всё это GNU-измы. Перед запуском чужого скрипта проверь, на чём он написан, или поставь coreutils.Код: Выделить всё
ls --color - readlink -f - на BSD исторически не было для канонизации пути. Бери
Код: Выделить всё
-fиз coreutils илиКод: Выделить всё
greadlink -f(он на маке есть).Код: Выделить всё
realpath - Путаница LSCOLORS и LS_COLORS - перенёс с linux переменную , а BSD ls её игнорирует и хочет
Код: Выделить всё
LS_COLORSсвоего формата.Код: Выделить всё
LSCOLORS - Глобальная подмена PATH через gnubin ломает чужие установщики, которые ждут BSD-поведения. Лучше явные g-команды.
- xargs: BSD xargs не имеет (разделитель). Используй
Код: Выделить всё
-dсКод: Выделить всё
-0или ставь GNU findutils (Код: Выделить всё
find -print0).Код: Выделить всё
gxargs
- Узнай версию системы и чип: и
Код: Выделить всё
sw_vers.Код: Выделить всё
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 - Найди файл двумя способами: рекурсивно и через Spotlight
Код: Выделить всё
find ~ -name t.txt 2>/dev/null. Заметь разницу в скорости.Код: Выделить всё
mdfind -name t.txt - Поставь GNU sed и сравни: . Убедись, что
Код: Выделить всё
brew install gnu-sed; gsed -i 's/baz/foo/' t.txt; which -a sedне требует пустой строки.Код: Выделить всё
gsed - Глянь зависимости бинаря: . Это твой ldd.
Код: Выделить всё
otool -L /bin/ls
- Почему ломается на macOS и как записать то же самое правильно?
Код: Выделить всё
sed -i 's/a/b/' f - Чем отличается от того, что нужно на маке, и какой флаг там вместо
Код: Выделить всё
stat -c?Код: Выделить всё
-c - Ты поставил coreutils. Как теперь вызвать GNU-версию ls и как проверить, какая утилита реально запускается под именем ?
Код: Выделить всё
ls - Какой командой посмотреть зависимости динамической библиотеки, раз на маке нет?
Код: Выделить всё
ldd
Запомни одну рамку: терминал на macOS - это BSD, и базовые утилиты это другие программы с теми же именами, что на Linux. Совпадает POSIX-ядро, расходятся надстройки -
Код: Выделить всё
sed -iКод: Выделить всё
statКод: Выделить всё
dateКод: Выделить всё
findКод: Выделить всё
lsКод: Выделить всё
-iКод: Выделить всё
-fКод: Выделить всё
-cКод: Выделить всё
-vКод: Выделить всё
-rКод: Выделить всё
otool -LКод: Выделить всё
lddКод: Выделить всё
manКод: Выделить всё
type команда