Z shell (zsh)
Z shell (zsh)
Zsh — это командная оболочка (shell) семейства Unix: программа, которая
читает то, что ты набираешь в терминале, разбирает это как язык, и
запускает нужные процессы. С 2019 года это shell по умолчанию во всех
новых macOS.
История
-
1990, США, Принстон. Аспирант Paul Falstad написал первую версию zsh
как учебный проект и в апреле 1990 выложил в usenet-группу
alt.sources. Имя — от логина другого студента, Zhong Shao, чей username
на почтовом сервере Принстона былzsh. Falstad увидел его в буфере
почтового клиента, подумал «звучит коротко» — и назвал так свою
оболочку. -
Начало 1990-х. Разработку подхватило сообщество. К 1996 году появилась
версия 3.0 — та самая, где сложилась привычная система файлов конфигурации
(.zshenv,.zshrc,.zprofile) и мощная система автодополнений
compinit. -
2004, версия 4.2. Сделали ту самую «умную» подсказку с меню
автодополнения через Tab, где стрелками можно листать варианты — фича,
которую сейчас копируют почти все современные shell. -
Октябрь 2019, macOS Catalina 10.15. Apple меняет shell по умолчанию
для всех новых пользователей сbashнаzsh. У этого шага одна
главная причина, которая не про технологию:bash 4.0(2009) вышел под
лицензией GPL v3, а Apple юридически несовместима с GPL v3 в своих
продуктах. Поэтому Apple 15 лет поставляла старуюbash 3.2от 2007
года — а потом решила уйти наzsh, у которого MIT-подобная лицензия и
можно спокойно распространять. -
2009, Robby Russell. Появился
oh-my-zsh— фреймворк из готовых
плагинов и тем, который сделал zsh модным среди разработчиков ещё до
того, как Apple выбрала его дефолтом. Сейчас у oh-my-zsh около 180 тысяч
звёзд на GitHub. -
Актуальный статус. Последний релиз —
5.9(май 2022). Разработку
ведёт группа мейнтейнеров, координируется через список рассылки
zsh-workers@zsh.org. Проект живой, но релизы редкие — оболочка
считается зрелой и стабильной.
Что это такое
Shell (оболочка) — это программа, которая работает переводчиком между
человеком и операционной системой. Ты набираешь текст, shell разбирает
его как язык программирования с очень своеобразным синтаксисом,
запускает нужные процессы, соединяет их пайпами (труба, |), подставляет
переменные окружения, разворачивает шаблоны имён файлов (*.md),
записывает вывод в файл. Всё, что происходит в терминале, — это работа
shell.
Zsh — не единственный такой инструмент. Есть sh (POSIX shell —
общеотраслевой стандарт), bash (bourne-again shell, дефолт большинства
Linux), fish (friendly interactive shell, ориентирован на удобство),
dash (минималистичный, быстрый, часто как /bin/sh в Debian/Ubuntu).
Все они говорят на диалектах одного семейства языков и в большинстве
случаев умеют выполнять почти любой чужой скрипт.
Zsh выделяется тем, что он одновременно:
- POSIX-совместим — то есть если ты напишешь
#!/bin/shи внутри код
на канонической shell-грамматике, zsh это выполнит правильно. - Расширяет POSIX — у него есть свой богатый диалект с массивами,
ассоциативными массивами (словарями), расширенными шаблонами
(**/*.log— рекурсивный поиск), плавающими числами, циклами со
спискообразным синтаксисом. - Интерактивно очень удобен — умное автодополнение по контексту, общая
история между вкладками, «беглый ввод» (пишешьcd, он подсказывает
ранее посещённые директории).
Легко перепутать zsh с oh-my-zsh или с темой powerlevel10k — но это
разные вещи. Zsh — сама оболочка. oh-my-zsh — сборник настроек и
плагинов поверх zsh. powerlevel10k — одна из тем визуального
оформления строки приглашения. Можно пользоваться zsh совсем без них.
Аналогии из жизни
Официант в ресторане. Ты не идёшь на кухню сам — ты называешь официанту,
что хочешь. Официант знает меню, переводит твои слова в заказ повару,
приносит блюда, а ещё запоминает твои предпочтения («как обычно, но без
лука»). Shell — точно такой же посредник между тобой и «кухней»
операционной системы.
Где ломается: официант ограничен меню и знанием ресторана. Shell же
может запустить вообще любую программу — ограничений «меню» нет, но зато
если программы нет, он молча скажет «command not found» и ничего умного
не подскажет.
Дирижёр оркестра. Оркестр — это отдельные программы (grep, awk,
sort, curl). Каждая умеет одну штуку. Дирижёр (shell) не играет сам,
но комбинирует их: сначала эта партия, потом та, а вот здесь труба (pipe)
передаёт звук из одной секции в другую. Красота Unix — именно в том, что
ты через shell собираешь сложный номер из простых инструментов.
Где ломается: у оркестра ноты синхронизированы. У shell — нет: если
одна программа в пайпе крашится, вторая может этого не заметить и
продолжить обрабатывать пустой поток. Отсюда легендарные баги
set -o pipefail в shell-скриптах.
Секретарь с блокнотом. У хорошего секретаря есть блокнот, куда он
записывает твои поручения, помнит контекст («вчерашний звонок Ивану —
это был по проекту N»), сам предлагает продолжения. Zsh с включённой
общей историей, автодополнением и подсказками — это тот же секретарь.
Где ломается: секретарь знает только то, что ты ему сказал. Shell тоже
не читает мысли — если ты не настроил zsh-autosuggestions, никаких
подсказок не будет. И общая история между окнами включается флагом,
дефолт — «каждое окно живёт своей историей».
Как это работает
Когда ты открываешь терминал и появляется приглашение (prompt) типа
~ $ — на самом деле там уже запущен процесс zsh, ждущий ввода. Что
происходит дальше по шагам:
1. Инициализация: файлы конфигурации. Zsh при запуске читает конфиги
в строгом порядке. Это важно — все грабли типа «переменная не
подхватывается» — обычно из-за путаницы в этих файлах:
Всегда, для любого запуска zsh:
/etc/zshenv → ~/.zshenv
Только для login-shell (первый вход, ssh, консоль):
/etc/zprofile → ~/.zprofile (до .zshrc)
Только для интерактивной сессии (терминал):
/etc/zshrc → ~/.zshrc
Только для login-shell:
/etc/zlogin → ~/.zlogin (после .zshrc)
При выходе из login-shell:
/etc/zlogout → ~/.zlogout
Практически: переменные окружения (PATH, токены API) держи в
~/.zshenv — их подхватит любой запуск zsh, включая скрипты, запущенные
через launchd на Mac. Настройки интерактивности (алиасы, тема, история,
плагины) — в ~/.zshrc, чтобы они грузились только для реальных
сессий и не тормозили автоматические задачи.
2. Разбор строки: лексер и парсер. Когда ты нажимаешь Enter, zsh
берёт твою строку и разбирает её как язык. Простая команда
ls -la ~/dir | grep foo превращается во внутреннее дерево:
pipeline
├── command: ls
│ args: [-la, /Users/pavel/dir] ← ~ развернуто в HOME
└── command: grep
args: [foo]
stdin: pipe от предыдущей команды
Между «строкой» и «деревом» происходит несколько преобразований:
подстановка переменных ($HOME), развёртывание тильды (~),
арифметика ($((2+2))), развёртывание фигурных скобок ({a,b,c}),
развёртывание шаблонов файлов (glob: *.txt, **/*.log), разбиение на
слова.
3. Word splitting — самая частая грабля. В bash по умолчанию
переменная в кавычках сохраняется как одна строка, а без кавычек —
разбивается на слова по пробелам. В zsh разбиения по словам без
кавычек НЕ происходит — это одно из главных отличий от bash.
Пример боевой ошибки из моего вчерашнего лога:
SSH_КОМАНДА="ssh -J root@1.2.3.4 -p 1905"
ХОСТ="neiro@86.62.125.34"
$SSH_КОМАНДА "$ХОСТ" # в zsh это НЕ ssh -J root@... -p 1905 neiro@...,
# а «попытка выполнить программу с именем
# 'ssh -J root@... -p 1905'» — крашится сразу.
Чтобы получить bash-подобное поведение, нужно писать ${=SSH_КОМАНДА} —
явный опт-ин в word splitting. Либо использовать массив (правильный
способ): cmd=(ssh -J root@1.2.3.4 -p 1905); $cmd "$хост".
Это классический капкан для тех, кто переходит на Mac после Linux: копирует
скрипт, всё вроде такое же, но что-то ломается — а разгадка в этих 15
буквах.
4. Запуск процессов. Для каждой команды zsh делает системный вызов
fork() (создать копию процесса) и execve() (заменить в копии код на
код нужной программы). Пайп между командами — это на самом деле файловый
дескриптор ядра: stdout первой программы приколочен к stdin второй.
5. Автодополнение (compinit). Когда ты нажимаешь Tab, срабатывает
подсистема completion. Zsh загружает описания команд из
/usr/share/zsh/*/functions/Completion/ (тысячи файлов, по одному на
популярную утилиту), смотрит контекст твоего ввода (какое слово,
предыдущие аргументы) и подсказывает варианты. Это то, из-за чего zsh
знает, что после git checkout идёт имя ветки, а после docker exec —
имя контейнера.
6. История и её магия. Zsh пишет каждую введённую команду в
~/.zsh_history. С правильно выставленными флагами (SHARE_HISTORY,
INC_APPEND_HISTORY) все окна терминала мгновенно видят историю друг
друга. Это одно из отличий от bash — там историю нужно пилить руками
через PROMPT_COMMAND.
Где встречается в обычной жизни
- Каждый Mac, купленный после 2019 года. Открываешь Terminal.app или
iTerm2 — тебя встречает zsh. Приглашение видаpavel@MacBook ~ %—
дефолтный prompt zsh на macOS. - Разработчики в кафе. Если видишь пёстрый терминал с иконками,
цветными ветками git и стрелками — это почти наверняка zsh с
powerlevel10kилиstarship. Дефолтный bash так не выглядит. - Инструкции в интернете. Половина туториалов начинается с
«добавьте в~/.zshrc» — потому что автор писал на Mac и не подумал
про Linux-читателя, у которого дефолтbash. - Проблемы с копи-пастой. Скрипт, который «работает у автора»,
падает у тебя — часто этоzsh vs bash: массивы индексируются
по-разному, word splitting, exit-код пайпов. - Умное дополнение в консоли. Когда ты пишешь
cd Do<Tab>и он
сразу предлагаетDocuments/,Downloads/с меню и стрелками —
этоcompinitzsh.
Где встречается в IT и бизнесе
- CI/CD скрипты. Многие пайплайны предполагают конкретный shell —
и если разработчик писал на zsh, а CI-раннер запускает
черезsh— привет разные глобы, разное поведение массивов, разные
exit-коды. Одна из причин, почему#!/bin/bashявно указывают в
первой строке скрипта. - Автоматизация локальных задач. Всё, что ты запускаешь на своей
машине черезlaunchd(Mac) илиcron— крутится в shell.
Понимание, какие переменные окружения подхватываются вlaunchd
(только из~/.zshenv, не из~/.zshrc), решает половину багов
«работает в терминале, не работает по расписанию». - Onboarding разработчиков. Первое, что делает опытный dev на
новом ноуте: устанавливает zsh + oh-my-zsh + свою тему + свои
алиасы. Это профессиональный «домик»: настройка~/.zshrc
переезжает с ним по всем работам. - DevOps и SRE. Написание одноразовых сложных пайпов
(ssh server | grep error | awk '{print $3}' | sort | uniq -c) —
повседневная работа. Zsh с расширенным глоббингом (**/*.log) сильно
экономит время: одна строчка вместоfind + xargs. - Git-flow. Плагины типа
git-open, красивый prompt с текущей
веткой, автодополнение git-команд по alias — стандарт для команд,
живущих в терминале. - Продукты с встроенным shell-режимом. Многие CLI-инструменты
(Kuberneteskubectl, AWS CLI,gcloud) поставляют completion-файлы
для zsh — это признание того, что среди пользователей CLI-инструментов
zsh уже мейнстрим.
Кто пользуется
- Apple. Официально ставит zsh дефолтом во всех новых macOS с 2019
года. Внутренние Mac разработчиков — тоже zsh. Приложение
Terminal.app в macOS показывает zsh при первом запуске. - GitHub, Stripe, крупные tech-компании. Внутренние onboarding-гайды
включают установку oh-my-zsh с корпоративной темой (я это встречал в
открытых репозиториях типаgithub/dotfiles). - Разработчики-одиночки и фрилансеры. Сообщество вокруг oh-my-zsh
оценивают в сотни тысяч человек — по числу активных пользователей
темыrobbyrussell(дефолт oh-my-zsh). Точных цифр никто не собирает,
но количество звёзд репозитория (180K+) даёт порядок. - Linux-энтузиасты. Хотя дефолт Linux — bash, среди тех, кто
«настраивает свою систему», zsh очень популярен. В Arch Wiki и
сообществе NixOS zsh упоминается как рекомендуемая альтернатива. - Обучающие ресурсы. Курсы по DevOps, книги про командную строку
(например, «The Linux Command Line» Уильяма Шоттса, 2019) — про
zsh пишут отдельные главы.
Альтернативы и конкуренты
bash (Bourne-again shell)
- Плюс: дефолт всех Linux-серверов, значит скрипт с #!/bin/bash работает
везде без сюрпризов; знать bash обязательно, если работаешь с серверами.
- Минус: интерактивная часть слабее (тупое автодополнение, скудная
история), новые версии под GPLv3 — юридический геморрой для
проприетарного софта.
fish (Friendly Interactive Shell)
- Плюс: из коробки красивый, умный, с подсветкой синтаксиса и
подсказками — нулевая конфигурация даёт то, за что в zsh платишь часом
настройки.
- Минус: не POSIX-совместим. Скрипты, написанные для sh/bash, не
выполнятся напрямую. Синтаксис оригинальный. Меньше комьюнити,
меньше плагинов.
dash / ash
- Плюс: маленький, быстрый; часто это /bin/sh в Debian/Ubuntu (для
системных скриптов).
- Минус: минимализм — почти ничего сверх POSIX; интерактивно неудобен,
никто в нём не сидит.
PowerShell (Microsoft, кроссплатформенный)
- Плюс: работает с объектами, а не с текстом (пайпы передают структурированные
данные — намного мощнее для обработки JSON, XML, .NET-объектов); есть
на Windows/macOS/Linux.
- Минус: чужеродный синтаксис для Unix-мира, тяжеловесен, стартует
медленнее zsh на секунду-две.
nushell (nu)
- Плюс: молодой (2019+), тоже объектный shell, крутая работа с CSV/JSON
из коробки. Красивые таблицы.
- Минус: пре-1.0, ломающие изменения между версиями, ещё не мейнстрим,
мало готовых сниппетов в интернете.
Когда НЕ стоит использовать
- Для системных скриптов на Linux-серверах. Пиши на
shили явно
#!/bin/bash. Zsh может быть не установлен на минимальном сервере
(Alpine, минимальный Debian), аshесть везде. Твой скрипт должен
работать в любой среде — не завязывай его на zsh, если можно писать
портируемо. - В контейнерах Docker с минимальным образом. Alpine Linux (5MB)
не включает zsh; установка добавит десятки мегабайт. Для образа
«запусти одну команду» — держись/bin/sh. - В скриптах, которые будут выполняться через
launchdна Mac.
Тут не про сам zsh, а про его конфиги:launchdне подгружает
ни~/.zshrc, ни~/.zshenvавтоматически. Скрипт, который «работает
в терминале», окажется вlaunchdбезPATH, без токенов, без всего.
Нужно либо явно подгружать~/.zshenvв скрипте (source ~/.zshenv),
либо не рассчитывать на shell-окружение. - Когда у команды нет унифицированного окружения. Если полкоманды
на Linux/bash, полкоманды на Mac/zsh — держите общие скрипты в
POSIX-совместимом sh, а личные ритуалы (алиасы, prompt) — в своих
~/.zshrc, которые остаются приватными.
Связанные понятия
- POSIX shell — стандарт, описывающий минимальный общий синтаксис
всех Unix-оболочек. Пишешь под POSIX — работает везде. - Bourne shell (sh) — прародитель, написан Стивеном Борном в Bell
Labs в 1977 году. Всё семейство (bash, zsh, ksh, dash) — потомки. - Bash (Bourne-Again Shell) — дефолт Linux, ближайший «брат» zsh
по функциям и синтаксису, но с массой мелких отличий. - Oh My Zsh — фреймворк-обвязка для zsh с плагинами и темами; часто
первое, что ставят вместе с zsh. - Powerlevel10k, Starship — популярные темы для строки приглашения;
показывают контекст (ветку git, версию языка, статус выполнения). - compinit — система автодополнения zsh, база «умных» подсказок Tab.
- Zsh Line Editor (ZLE) — редактор командной строки внутри zsh
(аналог readline у bash), отвечает за навигацию по строке, историю,
клавиатурные шорткаты. - [[launchd]] — планировщик macOS; популярный источник багов
«работает в терминале, не работает по расписанию» — из-за
неподгружаемого окружения zsh.
Литература и источники
- Официальный сайт:
zsh.sourceforge.io— тут дистрибутивы,
документация, история релизов. Проект хостится на SourceForge с 90-х. - Wikipedia:
en.wikipedia.org/wiki/Z_shell— коротко про историю и
особенности; русская версия тоже есть, но менее детальная. - Man-страница
zsh(1)и особенноzshexpn(1)(разбор
раскрытий) иzshoptions(1)(все опции) — сотни страниц полезного
референса. Читаешь черезman zshв терминале. - Книга «Learning the zsh Shell» — короткая книга O'Reilly, старая
(2003), но актуальная в базовой части. На английском. - Robby Russell, репозиторий
ohmyzsh/ohmyzshна GitHub — исходники
oh-my-zsh, полезно почитать плагины (например,git) чтобы понять,
как устроены алиасы и функции. - Peter Stephenson, «A User's Guide to the Z-Shell» — гайд от
одного из ключевых мейнтейнеров zsh, доступен на сайте проекта.
На английском, 200+ страниц, серьёзное чтение. - Habr и Medium: искать по запросу «zsh vs bash различия» или
«настройка oh-my-zsh» — тонны практических статей на русском.
Где встретилось у меня
Вчера настраивал HTTPS для нового домена на VPS через certbot, и
скрипт публикации publish_neiro3.sh упал на конструкции
$SSH_КОМАНДА "$ХОСТ" — потому что zsh, в отличие от bash, не
разбивает переменную на слова без явного ${=var}. Отдельно — про
кириллические имена переменных: zsh их принимает нормально, а bash — нет.
Оба случая — мелкие особенности shell, которые бьют по времени именно
тогда, когда о них забываешь.
Краткое резюме
- Zsh — shell по умолчанию в macOS с 2019 года; заменил bash не столько
из-за лучшей функциональности, сколько из-за несовместимости Apple с
лицензией GPLv3. - Умеет всё, что bash, плюс сильно удобнее интерактивно: умное
автодополнение, общая история, продвинутые глобы, ассоциативные
массивы. - Главные грабли перехода с bash: нет word splitting без кавычек
(нужен${=var}), массивы индексируются с 1, конфиги другие
(~/.zshrc,~/.zshenv). - В скриптах, которые должны быть портируемы, пиши под POSIX (
#!/bin/sh)
или явный bash. Оставляй zsh для личной интерактивной работы. - Экосистема:
oh-my-zsh(плагины),powerlevel10k/starship(темы).
Дефолтный zsh без обвязки — тоже отличный shell, но красивым сам не
станет.