Shell & CLI Craft
«У нас всё на Python / Go, bash — это legacy для боли» — позиция, после которой команда тратит полчаса на трансформацию, которую awk '{print $2}' | sort | uniq -c | sort -rn делает за 5 секунд. Shell — это не «костыли», а инструмент композиции: 50 лет unix-tools работают как одинаковые входы и выходы, и их можно собрать в нужный конвейер прямо в терминале, без проекта на 200 строк. SRE без shell-беглости в on-call наполовину слеп: пока он пишет Python-script для парсинга логов, инцидент уже погасили те, кто grep | jq | sort за минуту. Лист — про беглость в ~30 ключевых tools и про дисциплину «когда shell — правильный выбор, а когда — программа».
Граница: Programming Languages — выбор «настоящего» языка для сервисов и инструментов (Go / Python / Rust); shell — для композиции существующих utilities. CI/CD — где shell-скрипты живут production-style (pinned, linted, tested). Этот лист — про craftsmanship самого shell.
Что должен уметь
Заголовок раздела «Что должен уметь»Главный навык на уровне L4 — знать где shell кончается, а где начинается язык. Я регулярно вижу обе крайности: одни пишут 200-строчные bash-monsters с function, getopts, trap (это уже Python с худшим синтаксисом — лучше переписать), другие тянут Python для одностроки awk '{sum += $1} END {print sum}' (overkill). Правило, которое работает на практике: если в скрипте появляется три или больше команд if, или сложная структура данных, или нетривиальные ошибки, — это уже не shell. Если задача — «прогнать поток через 4 трансформации» — это shell, и любой Python будет проигрывать.
L3
- Свободно использует базовый набор:
grep(с-r,-E,-l,-c),find,sort,uniq,wc,head/tail,cut,tr,xargs. Пишет однострочные pipelines для ad-hoc анализа без выхода в редактор. - Знает разницу между
'single'и"double"quoting; не цитирует переменные через$var, а через"$var"(или"${var}"); проверяет результат через ShellCheck.
L4
- Бегло пишет
awkдля пост-обработки колоночных данных (одно из самых underused tools в индустрии); читаетsedбез открытия документации для простых замен. - Использует
jqдля JSON (де-факто стандарт в API-driven debugging); понимает--arg,select(),map(),to_entries. - Пишет on-call shell scripts с минимальной hygiene:
set -euo pipefail,trapдля cleanup, явный exit codes, явная error messages вstderr(>&2).
L5
- Различает interactive shell (короткие команды, alias’ы, history) и scripting shell (defensive style); не путает стиль одного с другим. Pipefail / nounset / errexit — defaults для scripts, не для terminal.
- Знает modern alternatives и осознанно выбирает:
rg(ripgrep) вместоgrep -r,fdвместоfind,batвместоcatдля интерактивного просмотра,dufвместоdf. Понимает, когда modern tool оправдан, а когда POSIX-портабельность важнее. - Применяет
xargs -Pдля параллелизации;parallelдля более сложных случаев; понимает, когда concurrency в shell ломает мысль и пора уходить в полноценный язык.
L6+
- Выстраивает team-level shell discipline: pinned tool versions в CI / devcontainer, shared profile / aliases, shellcheck в CI, naming convention для on-call scripts.
- Стратегически: какие задачи в org-shell library (shared bin/, terraform exec scripts, monitoring-pull scripts) — а какие настало время мигрировать в полноценный сервис.
Материалы
Заголовок раздела «Материалы»- Brian Kernighan, Rob Pike — The Unix Programming Environment (Prentice Hall, 1984). 40 лет, и не устарела. Глава 4 «Filters» — лучший introduction в композицию unix-tools. Если выбирать одну книгу — эту.
- Eric S. Raymond — The Art of Unix Programming (Addison-Wesley, 2003). Не tutorial по shell, а философия: «write programs that do one thing well», «text streams are universal interface». Полезно для понимания, почему shell-tooling выживает 50 лет.
- Cameron Newham — Learning the bash Shell (O’Reilly, 3-е изд., 2005). Канонический справочник по bash specifically. Не для линейного чтения, а для lookup.
- Dave Taylor, Brandon Perry — Wicked Cool Shell Scripts (No Starch Press, 2-е изд., 2017). Сборник реальных scripts с разбором; полезен как идиоматический pattern reference.
Статьи и доклады
Заголовок раздела «Статьи и доклады»- Google Shell Style Guide. Если выбирать одну shell style guide — эту. Прагматичная, не «теоретическая». Все рекомендации обоснованы. По моим наблюдениям, чаще всего org-level shell standards вырастают из неё.
- ShellCheck Wiki. Не статья, а коллекция объяснений common shell-ошибок. Каждое предупреждение ShellCheck — отдельная страница с подробным объяснением. Лучший способ учиться bash — читать wiki после первого фейла своего скрипта.
- Robert Mecklenburg — Managing Projects with GNU Make (O’Reilly, 3-е изд., 2004). Make — не shell, но используется вместе. Глава про автоматизацию workflow в команде через Makefile — практическая.
- The Art of Command Line. Curated cheat sheet на GitHub (~50k stars). Хорошая точка proof-of-knowledge: если 60% содержимого вам не знакомо — есть, что доучить.
Инструменты
Заголовок раздела «Инструменты»- bash — POSIX-совместимый default. Если выбирать один shell для скриптов — bash. Zsh лучше для интерактивной работы, но в scripts лучше bash для portability.
- ShellCheck — линтер для shell. Обязательный pre-commit / CI hook для любого репозитория с .sh файлами. Ловит подавляющее большинство bash gotchas.
- jq — де-факто стандарт для JSON в shell. По моим наблюдениям, навык jq на уровне
select() | map()отличает рядового SRE от senior’а в on-call ситуациях с REST API. - yq — то же для YAML; критично для kubernetes-команд (помогает читать manifests, helm-values).
- ripgrep (rg) / fd / bat / delta — modern замены
grep -r/find/cat/ git diff viewer. По моим наблюдениям, чаще выбирают именно эту четвёрку для personal setup. - GNU parallel / xargs -P — параллелизация.
parallelмощнее, но learning curve выше;xargs -Pхватает в 80% случаев. - Modern Unix — curated list современных альтернатив classic unix-tools. Полезно для periodic refresh personal toolchain.
- Анти-инструмент: «300-строчный bash-script с function / case / getopts». Это уже не shell — лучше переписать в Python / Go.
Best practices
Заголовок раздела «Best practices»Главный публичный кейс — не отдельный инцидент, а феномен jq и kubectl: jq за ~10 лет превратился из niche-tool в обязательный навык для любого, кто работает с REST API; kubectl ... -o json | jq — самая частая идиома в kubernetes-on-call. Это пример того, как композиция простых tools доминирует над «built-in complex tools»: вместо того чтобы добавлять в kubectl полноценный query-language, индустрия сошлась на «kubectl output JSON → jq делает остальное». Я регулярно вижу команды, в которых senior’ы за минуту получают ответ на сложный k8s-вопрос через kubectl get ... -o json | jq '...', а junior’ы за полчаса пишут Python с k8s client library под тот же вопрос. Это не критика junior’ов — это пример того, насколько мощна «cheap composition» через shell, когда инженер беглый.
Короткие правила:
set -euo pipefail— defaults для каждого scripted файла. Безerrexitошибка в середине ломает invariants; безnounsettypo в имени переменной превращается в""; безpipefailfailure в начале pipeline теряется. Не нравится — пиши защиту явно, но не молча игнорируй.- Quote variables:
"$var", не$var. Без кавычек слова со spaces / glob characters ломают код. ShellCheck ловит это автоматически — не игнорируй. - Three-
ifправило: если в скрипте появляется три или больше условных ветвей или нетривиальная структура — это уже не shell. Перепиши в Python / Go. Долгие bash-monsters — это тех-долг, который рос годами; здоровая команда вовремя останавливается.
Подробнее:
awk — самый underused tool в индустрии. Огромная часть SRE-задач — это «возьми колоночный output, посчитай сумму / уникальные / отсортируй по N-й колонке». В Python это 10 строк, в awk '{sum[$1] += $2} END {for (k in sum) print k, sum[k]}' — одна строка прямо в pipe. По моим наблюдениям, разница между беглым и не-беглым в shell — почти всегда awk-fluency. Это не «выучить awk целиком» (язык богатый, легко утонуть) — это знать ~10 идиом наизусть. Один день, заметная разница на годы.
Interactive vs scripted shell — разный стиль. Распространённая ошибка — писать scripts стилем terminal-сессии (без quoting, без error handling, без set -e) или, наоборот, в terminal писать defensive ad-hoc команды (теряется скорость). В terminal: короткие, можно ошибиться, history спасёт. В scripts: pipefail / nounset, явный error handling, ShellCheck’нуто. Я регулярно вижу команды, в которых эта разница не сформулирована — и script-quality плавающая.
On-call shell-library заменяет «волшебные знания в голове». В зрелой SRE-команде в shared repo лежит ~20–40 скриптов: «дать список pods в bad state по cluster», «exec в случайный pod по label», «достать last 50 stack traces из service logs». Это активы команды, не личный setup. По моим наблюдениям, разница между новичком и senior’ом в той же команде сокращается на месяцы, если такая library существует и поддерживается. Без неё каждый новый человек заново изобретает свои pipelines.
Pin tool versions для production scripts. Bash-script, использующий awk, работает на BSD awk иначе, чем на GNU awk. То же с sed, grep -P, date (BSD vs GNU). Production scripts в CI пинуют tooling через container image / asdf / nix; не пинуют — получают «работало на моём ноуте, ломается в CI». Это не bash gotcha — это discipline of supply chain для shell scripts.
Связанные листья
Заголовок раздела «Связанные листья»- Programming Languages — выбор настоящего языка для сервисов / tools (Go / Python / Rust); shell — для композиции и ad-hoc. Граница: три if’а — переходи в язык.
- CI/CD — shell-scripts в pipeline должны быть pinned, linted (ShellCheck), tested. CI — где scripted-style critical.
- Operating Systems — shell — главный интерфейс к OS; знание
/proc,/sys, syscall tracing (strace) — overlap с OS knowledge. - Networking —
tcpdump,ss,dig,curl -v,mtr— все сетевые tools — это shell-композиция; shell-беглость — пре-условие для networking-беглости. - Runbooks — runbook шаги часто включают shell-команды; их качество (правильно ли quoted, есть ли error handling) определяет real-incident usability.
- Performance & Profiling —
perf,strace,ltrace,bpftrace— CLI-инструменты; profiling-беглость требует shell-беглости. - Toil Tracking — recurring shell-команды — кандидат на shared script в team library; tracking ловит signal «эту pipeline-команду набирали 20 раз — пора в bin/».
Открытые вопросы
Заголовок раздела «Открытые вопросы»- Zsh vs Bash для команды — для personal terminal zsh обычно выигрывает; для shared scripts — bash для portability. Но граница «где terminal становится скриптом» нечёткая.
- Modern shell альтернативы (TBD) — fish / nushell / oil. Стоит ли в SRE-команде переходить — открытый вопрос, общепринятой позиции нет.
- CLI design для собственных tools — отдельная подтема (когда команда пишет свой CLI: Cobra / Click / argparse, exit codes, output format). Возможно отдельный лист на стыке Programming Languages.
- Я не уверен, как тестировать shell-scripts правильно. Bats / shunit2 существуют, но adoption низкий; на практике большинство bash-scripts не имеют tests. Если у вас есть рабочая practice — расскажите через PR.