Если вы получили sed: 1: "s/foo/bar/g": invalid command code, ничего не сломалось. В macOS стоит BSD-окружение, а скрипт написан под GNU. Имена команд те же, флаги разные. Вот шесть различий, которые реально кусаются.
1. sed -i на macOS требует аргумент
$ clai замени foo на bar прямо в файле config.txt, чтобы работало и на маке, и на линуксе→ sed -i.bak 's/foo/bar/g' config.txt # переносимо: создаст config.txt.bak
В GNU суффикс бэкапа необязателен, в BSD — обязателен. sed -i '' 's/…' работает на macOS и падает на Linux. sed -i.bak работает везде — просто останется бэкап, который надо удалить.
2. Расширенные регулярки: только -E, никогда -r
$ clai замени любую последовательность цифр на N через расширенные регулярки→ echo "foo123" | sed -E 's/[0-9]+/N/'fooN
-r есть только в GNU. -E понимают обе версии — GNU принимает его уже много лет. Сделайте -E привычкой, и целый класс ошибок исчезнет.
3. date: -d на Linux, -r на macOS
$ clai преобразуй unix-время 1735689600 в читаемую дату в UTC→ date -u -d @1735689600 '+%Y-%m-%d %H:%M:%S UTC' # GNU2025-01-01 00:00:00 UTC→ date -u -r 1735689600 '+%Y-%m-%d %H:%M:%S UTC' # BSD / macOS
Переносимого варианта здесь нет. Либо ветвитесь по uname, либо ставьте GNU coreutils и вызывайте gdate.
4. stat: -c на Linux, -f на macOS
$ clai выведи размер и имя этого файла→ stat -c '%s %n' t.txt # GNU4 t.txt→ stat -f '%z %N' t.txt # BSD / macOS
Отличается и флаг, и спецификаторы формата. Если скрипт делает со stat что-то нетривиальное, возьмите find -printf на Linux или wc -c там, где нужна переносимость.
5. grep -P на macOS не существует
$ clai вытащи из этой строки только цифры, переносимым способом→ echo "abc123" | grep -Eo '[0-9]+'123
grep -P (perl-регулярки) — опция сборки GNU, в macOS её не поставляют. -E закрывает большинство реальных шаблонов; если действительно нужен lookahead, вам нужен perl или ripgrep, а не grep.
6. xargs -r есть только в GNU — и он вам нужен
$ clai найди .log старше 30 дней и удали их, не запуская rm на пустом списке→ find . -name '*.log' -mtime +30 -print0 | xargs -0 -r rm
Без -r GNU-шный xargs на пустом вводе запускает команду один раз вообще без аргументов — именно так xargs rm в пустом каталоге превращается в ошибку, а xargs docker rm — в сюрприз. BSD-шный xargs и так не запускается на пустом вводе, поэтому там -r не нужен, а в старых версиях ещё и не распознаётся.
Грабли
brew install coreutilsставит близнецов с префиксомg—gsed,gdate,gstat. Если добавитьgnubinв PATH, мак начнёт вести себя как Linux: у вас всё почините, а от остальной команды баг спрячете.- На
readlink -fполагаться нельзя. В старых macOS его нет. Беритеrealpath, где он есть, илиgreadlink -f. base64 -dпротив-D. GNU декодирует через-d, macOS исторически хочет-D.
Похожие вопросы
Что вообще значит «invalid command code»? BSD-шный sed съел следующее слово как суффикс бэкапа, а остаток вашего выражения прочитал как команду, которой не знает. Это жертва разбора аргументов, а не проблема регулярки.
macOS — это BSD или Linux? Ни то, ни другое. Пользовательское окружение ведёт родословную от FreeBSD, поэтому имена утилит совпадают с линуксовыми, а флаги — нет.
Как написать скрипт, который работает и там, и там? Держитесь POSIX-флагов, предпочитайте -E, всегда пишите sed -i.bak и проверяйте на обеих системах до того, как это попадёт в CI.
Читайте также
- Тот же флоу на Windows: PowerShell и cmd на равных
- Разбор сети по-человечески: соединения, порты, DNS
- От однострочника к закоммиченному скрипту: когда пора
CliAI определяет вашу ОС и оболочку и генерирует те флаги, которые эта машина действительно принимает. Установка одной строкой.