Школа

← К программе

Когда bash заканчивается: выбор инструмента

Урок курса «Курс подготовки DevOps-инженеров»

Хронометраж: занятие 60 минут — теория 30 минут, практика 30 минут. Домашняя работа: 1–2 часа.

Кратко о чём урок: Критерии перехода на Python: структуры данных, сетевые вызовы, обработка ошибок, тестируемость, размер скрипта. Стоимость поддержки как главный аргумент.


1. Что изучаем

  • Выбирать между bash и Python по признакам задачи, а не по привычке.
  • Узнавать признаки того, что bash-скрипт вырос за свои границы.
  • Измерять то, что можно измерить: время запуска, объём кода, поведение на неожиданном входе.
  • Переписывать скрипт, не меняя его интерфейс, чтобы переписывание не разрослось.
  • Объяснять решение так, чтобы через год его можно было пересмотреть осознанно.

2. Зачем это нужно и где применяется

Блок 04 закончился скриптами, которые не страшно поставить в cron. Этот блок про то, что происходит дальше: скрипт живёт, обрастает условиями, и в какой-то момент дописывать его становится дороже, чем переписать. Задача урока — научиться замечать этот момент и не путать его с двумя ошибочными крайностями.

Первая крайность — переписывать рано. Скрипт из пяти строк, который вызывает curl и grep, переписывают на Python «чтобы по-нормальному», и получают двадцать строк, зависимость от окружения и более медленный запуск. Работы стало больше, пользы — никакой.

Вторая, и более дорогая, — не переписывать никогда. Bash-скрипт разрастается до трёхсот строк, внутри уже jq со своей логикой и awk со своей, кавычки расставлены методом подбора, а || true стоит в пяти местах, потому что иначе set -e мешает. Такой скрипт работает, пока его не нужно менять. Первое же изменение занимает день и ломает что-то в другом месте.

Третья задача урока — уметь объяснить решение. «Мне так привычнее» не годится: скрипт переживёт вас в этой роли, и человек, который его унаследует, должен понимать, почему выбран этот инструмент и при каком условии решение стоит пересмотреть.

3. Теория (30 минут)

У чего какая сильная сторона

Bash — это язык склейки программ. Его сила в том, что вызов внешней команды выглядит как обычное слово, а конвейер соединяет программы без кода вообще. Пока задача формулируется как «взять вывод одной программы, отфильтровать и отдать другой», bash не имеет конкурентов по краткости.

Python — язык работы с данными и логикой. Его сила в том, что данные остаются структурами, а не текстом: словарь остаётся словарём, число — числом, ошибка — исключением с типом и местом возникновения.

Отсюда первый практический признак: посмотрите, что делает ваш скрипт между вызовами внешних программ. Если почти ничего — это bash. Если между вызовами идёт разбор, накопление, ветвление по данным — это уже Python, даже если сейчас скрипт короткий.

Сколько стоит запуск

Начнём с того, что легко измерить и что обычно переоценивают. Три прогона по 50 повторов в контейнере:

прогон   bash       python     python+импорты
1        5 мс       36 мс      100 мс
2        3 мс       21 мс      97 мс
3        2 мс       21 мс      98 мс

Python запускается в 7–10 раз дольше bash, а с импортами json, csv, pathlib, urllib.request — в 30–40 раз. Первый прогон всегда хуже: файлы ещё не в кэше страниц.

Когда это имеет значение? Только если скрипт вызывается часто: в цикле по тысяче файлов, в хуке на каждый коммит, в проверке мониторинга раз в десять секунд. Тысяча вызовов — это 2 секунды на bash и полторы минуты на Python с импортами. А если скрипт запускается раз в сутки, разница в 100 мс не значит ничего, и выбирать надо по другим признакам.

Скорость работы: результат против ожиданий

Теперь то, что обычно ожидают наоборот. Файл из 20 000 строк лога, две задачи, одинаковый ответ в обоих вариантах:

Задача bash Python
посчитать строки с ERROR grep -c — 7 мс 56 мс
сгруппировать по сервису и коду, посчитать среднее awk — 40 мс 189 мс

Bash и awk быстрее в 4–8 раз. Это честное сравнение: обе версии дали одинаковый результат (1449 записей, среднее 446.6), а сравнивать по скорости можно только способы, дающие одинаковый ответ — иначе быстрее оказывается тот, кто делает меньше работы.

Вывод, который стоит запомнить: переход на Python оправдан не скоростью. grep и awk написаны на C и десятилетиями оптимизировались под обработку текста построчно. Аргумент за Python другой — стоимость поддержки.

Объём кода: и здесь bash короче, но не всегда

Возьмём одну задачу — разобрать ответ API и посчитать работающие машины по зонам — и напишем обе версии аккуратно. Не «как получится», иначе сравнение нечестное.

Файл Строк кода (без пустых и комментариев)
summary.sh 7
summary.py 33

Bash короче в 4,7 раза. Часть разницы — документация и аннотации типов, которых в bash-версии нет вовсе, но и это показательно: в Python их естественно писать, в bash — некуда.

А теперь задача чуть сложнее: запрос к API с повторами при временных ошибках, с разбором кодов ответа и экспоненциальной отсрочкой.

Файл Строк кода
fetch-retry.sh 28
fetch_retry.py 52

Соотношение упало с 4,7 до 1,9. Вот это и есть критерий перехода, выраженный числом: чем больше в задаче логики, а не склейки, тем меньше преимущество bash в объёме. В какой-то точке кривые пересекаются, и дальше bash-версия становится длиннее и заметно менее понятной.

Пять мест, где bash ломается

Это не придирки, а измеренное поведение — каждое встречается в работе.

Арифметика. Bash умеет только целые числа:

bash: 7/2 = 3
bash: 0.1+0.2 → syntax error: invalid arithmetic operator
python: 7/2 = 3.5
python: 0.1+0.2 = 0.30000000000000004

У Python своя особенность двоичных дробей (последняя строка), но он хотя бы считает. Любой процент, среднее время ответа или размер в гигабайтах в bash требует внешней программы.

Имена файлов. Три файла, у одного пробел в имени, у другого перевод строки:

файлов создано: 4                        ← ls | wc -l насчитал 4 вместо 3
через for в подстановке: 5 элементов     ← for f in $(ls *.log)
через шаблон: 3                          ← for f in *.log
python: 3                                ← pathlib.Path.glob('*.log')

Первая строка сама по себе поучительна: ls | wc -l соврал из-за перевода строки в имени. Второй способ — классическая ошибка, разбитая по пробелам. Правильный способ в bash есть (шаблон), но помнить о нём надо всегда, а в Python перебор безопасен по умолчанию.

Структуры данных. В bash есть массивы и ассоциативные массивы, но только со строками и без вложенности: массив внутри массива невозможен. Как только данные становятся деревом — ответ API, конфигурация, разбор YAML — представлять их в bash нечем.

Форматы. JSON без внешних программ в bash не разобрать. С jq можно, но это уже второй язык в вашем скрипте, со своим синтаксисом и своими ловушками (в блоке 04 мы видели, как подстановка значения прямо в фильтр меняет смысл запроса). В Python это одна строка стандартной библиотеки.

Обработка ошибок. Самое неприятное. Вот скрипт с полным набором предохранителей:

set -euo pipefail
f() { grep -c 'нет-такой-строки' /etc/hostname; }
if f > /dev/null; then echo "найдено"; else echo "не найдено"; fi
count=$(f || true)

Результат: count='0'. Значение есть, скрипт продолжился, и всё выглядит нормально. Но grep -c вернул 0 совпадений и код 1, а || true этот код замаскировал. Если бы grep упал по-настоящему — не нашёл файл, не хватило прав, — count тоже оказался бы пустым или нулевым, и отличить «ноль совпадений» от «ошибки» уже невозможно.

В Python неудача — это исключение с типом и местом:

bash+jq:  jq: error (at instances-broken.json:1): Cannot iterate over null (null)   код 5
python:   TypeError: 'NoneType' object is not iterable                              код 1

Оба сообщили об ошибке, и это важно отметить честно: jq тоже не молчит. Разница в том, что в Python видно тип ошибки, и на разные ошибки можно ответить по-разному, а не только «упало — вышли».

Тестируемость — самый недооценённый аргумент

Функция в Python-версии вызывается напрямую:

python: функция summarize вызвана напрямую из теста, утверждение прошло

Это два десятка символов в тесте: импортировать модуль, вызвать функцию, сравнить результат. Логику можно проверить без запуска скрипта целиком и без обращения к сети.

В bash функцию из конвейера отдельно не вызвать. Тестировать можно (в блоке 04 мы делали это через bats), но это тестирование скрипта целиком с подменой внешних команд — заметно дороже и хрупче.

Почему это важно именно для эксплуатации: скрипт, который что-то удаляет или отправляет запросы в облако, хочется проверить до того, как он это сделает. Тест на функции даёт такую возможность, а «запустить и посмотреть» — нет.

Заодно в Python-версии видно ещё одно свойство: список повторяемых кодов ответа — это данные, множество {429, 500, 502, 503, 504}, а не текст внутри case. Данные можно проверить в тесте, вывести в журнал, вынести в конфигурацию.

Красные флаги: когда пора

Собранный по опыту список признаков, что скрипт вырос из bash. Каждый — сигнал, что стоимость поддержки растёт быстрее пользы.

  • внутри есть jq или awk со своей логикой — то есть уже два языка в одном файле;
  • есть || true, чтобы set -e не мешал, и непонятно, что при этом маскируется;
  • кавычки расставлены методом подбора, и страшно менять;
  • функции нельзя проверить отдельно, только запуском всего скрипта;
  • при странном входе скрипт продолжает работать и портит данные;
  • число вложенных if в for внутри while больше двух;
  • разбор JSON или YAML сделан через grep и sed.

Три и больше — задача уже за границей, где bash дешевле.

И обратный список, признаки того, что переписывать не надо: скрипт короче пятидесяти строк, логики между вызовами почти нет, он не менялся полгода и запускается часто. Работающий короткий bash-скрипт — не долг, а нормальный инструмент.

Цена Python, о которой надо знать заранее

Выбирая Python, вы берёте на себя вопрос зависимостей. В стандартной библиотеке есть многое, но не всё:

Модуль Где
json, csv, pathlib, sqlite3, urllib.request стандартная библиотека
tomllib стандартная библиотека, с версии 3.11, только чтение
yaml внешний пакет PyYAML
requests, httpx внешние пакеты

Разница существенная: скрипт, которому хватает стандартной библиотеки, запускается на любом сервере без подготовки. Как только появляется requests, добавляется работа — виртуальное окружение, фиксация версий, установка на целевой машине. Причём просто поставить пакет в системный Python в Debian не получится:

$ python3 -m pip install PyYAML
error: externally-managed-environment
This environment is externally managed

Это не поломка, а защита от смешивания системных и своих пакетов (PEP 668). Правильный путь разбирается в уроке 06.04. Здесь важно другое: при выборе инструмента учитывайте не только удобство языка, но и то, что придётся сделать для запуска на сервере.

3.1. Примеры и варианты реализации

Одна задача — три среды. Если вариант в этом уроке неприменим, это указывается явно с объяснением причины.

Среда Как решается Что нужно иметь
Локальный стенд (Docker / VM / kind) оба варианта пишутся и сравниваются рядом, замеры повторяются bash, python3, jq; образ блока
VPS / bare-metal здесь решение проверяется на практике: что уже установлено и что придётся ставить доступ к серверу, где скрипт будет работать
Yandex Cloud выбор часто определён тем, есть ли готовый SDK и как устроен API доступ к облаку, токен

Локальный стенд. Примеры к уроку — две пары скриптов, решающих одну задачу на bash и на Python, плюс скрипт для замера времени запуска. Смысл не в том, чтобы прочитать их, а в том, чтобы запустить, сравнить вывод командой diff и посмотреть на числа. Образ описан в examples/docker/ — там же jq, ruff и mypy, которыми проверяются примеры.

VPS / bare-metal. На сервере выбор упирается в то, что там уже есть. Bash есть всегда. Python 3 в современных дистрибутивах тоже есть почти всегда, но без pip и без venv: в Debian это отдельные пакеты (python3-pip, python3-venv), и на минимальной установке их нет. Значит скрипт на стандартной библиотеке скопируется и запустится, а скрипт с requests потребует подготовки — и это надо учесть при выборе, а не обнаружить при развёртывании.

Второй практический момент: если скрипт запускается из cron, помните про урезанное окружение из урока 04.08. Python это не лечит — PATH там будет таким же коротким, и путь к интерпретатору лучше указывать полностью.

Yandex Cloud. Здесь выбор часто решается за вас наличием готовых средств. Для облачных API есть CLI (yc), который отдаёт JSON, и есть SDK для Python. Практическое правило: если задача — «вызвать одну команду и посмотреть результат», берите CLI и bash. Если задача — «пройти по всем ресурсам, сопоставить с данными из другого источника, собрать отчёт», берите SDK и Python: обход пагинации и обработка ошибок на bash быстро превращаются в тот самый скрипт с семью красными флагами.

Отдельно: у CLI и SDK разные способы аутентификации и разные форматы ошибок. Это ещё один аргумент не смешивать их в одном скрипте — выбрать что-то одно.

Что выбрать. Правило, которое работает: пишите на bash, пока задача остаётся склейкой готовых программ, и переходите на Python, когда появляется логика между вызовами. Не по числу строк, а по характеру работы. И фиксируйте решение письменно — с условием, при котором его стоит пересмотреть.

4. Практика на занятии (30 минут)

Курс проходят самостоятельно, поэтому каждый шаг говорит, что ввести, зачем, что должно получиться и что делать при неудаче.

Что делаем. Измеряем то, о чём обычно спорят, сравниваем две реализации одной задачи и учимся замечать границу.

Файлы для работы: blocks/06-python/examples/06.01-bash-or-python/

Шаг 1 (5 минут). Измерьте время запуска на своей машине.

  • Что ввести:

    cd blocks/06-python/examples/06.01-bash-or-python
    ./bench-startup.sh
    

    Ставить что-либо не обязательно: образ с оснасткой блока описан в examples/docker/ — соберите course-py:13 и запустите docker run --rm -it -v "$PWD:/work" -w /work course-py:13 bash.

  • Зачем: увидеть своими числами, во сколько раз запуск Python дороже запуска bash.

  • Ожидаемый результат: bash в единицах миллисекунд, Python в десятках, Python с импортами — около сотни. Абсолютные числа у вас будут свои, важно соотношение.

  • Если не так: если Python вышел быстрее bash — почти наверняка мешает холодный кэш страниц; повторите прогон, первый всегда хуже. Команда не найдена — вы не в каталоге примера или не собран образ course-py:13.

Шаг 2 (5 минут). Сравните две реализации одной задачи.

  • Что ввести:

    ./summary.sh instances.json
    ./summary.py instances.json
    diff <(./summary.sh instances.json) <(./summary.py instances.json) && echo "вывод совпадает"
    
  • Зачем: убедиться, что обе версии дают одинаковый результат — только тогда их честно сравнивать по объёму кода.

  • Ожидаемый результат: одинаковый вывод и строка вывод совпадает от diff.

  • Если не так: diff показал строки — версии разошлись; сначала найдите, какая права, сравнивать объём рано.

  • Ответьте: посмотрите оба файла и посчитайте строки: grep -vcE '^\s*$|^\s*#' summary.sh summary.py.

Шаг 3 (5 минут). Посмотрите, что происходит на битом входе.

  • Что ввести:

    ./summary.sh instances-broken.json; echo "код: $?"
    ./summary.py instances-broken.json; echo "код: $?"
    
  • Зачем: увидеть, чем отличается реакция на ошибку у bash+jq и у Python.

  • Ожидаемый результат: обе версии сообщают об ошибке, но по-разному — разные сообщения и коды возврата. Обратите внимание, что jq тоже не молчит: разница не в наличии ошибки, а в том, что с ней можно сделать дальше.

  • Если не так: если какая-то версия завершилась с кодом 0 и без сообщения — битый вход не дошёл до разбора; проверьте, что подан именно instances-broken.json.

Шаг 4 (6 минут). Сравните задачу посложнее.

  • Что ввести:

    ./fetch-retry.sh https://example.com/ | head -3
    ./fetch_retry.py https://example.com/ | head -3
    ./fetch-retry.sh https://example.com/net-takoy-stranicy
    ./fetch_retry.py https://example.com/net-takoy-stranicy
    
  • Зачем: проверить, как меняется соотношение объёма кода, когда в задаче появляется логика — повторы и разбор кодов ответа.

  • Ожидаемый результат: на живом адресе обе работают, на 404 обе не повторяют запрос.

  • Если не так: нет доступа в сеть наружу — обе версии не получат ответ; тогда сравнивайте только объём кода, а не поведение.

  • Ответьте: посчитайте строки в обеих версиях и сравните соотношение с шагом 2 — оно должно заметно упасть.

Шаг 5 (5 минут). Вызовите функцию из Python-версии напрямую.

  • Что ввести:

    python3 -c "
    import sys; sys.path.insert(0, '.')
    from summary import summarize
    counts, cores = summarize('instances.json')
    assert counts['ru-central1-a'] == 2, counts
    print('логика проверена без запуска скрипта целиком')"
    
  • Зачем: показать, что логику Python-версии можно проверить тестом, не запуская скрипт целиком и не обращаясь к сети.

  • Ожидаемый результат: проверка проходит, печатается логика проверена без запуска скрипта целиком.

  • Если не так: ModuleNotFoundError — вы не в каталоге примера (вернитесь cd из шага 1); AssertionError — во входных данных не два узла в зоне, посмотрите instances.json.

  • Ответьте: подумайте, как сделать то же с summary.sh, и во что это превратится.

Шаг 6 (4 минуты). Заполните таблицу критериев на своей задаче.

  • Что ввести: возьмите любой свой bash-скрипт (или скрипт из блока 04) и пройдите criteria-table.md, включая список красных флагов.
  • Зачем: перенести критерии урока на реальную задачу и получить обоснованное решение, а не ощущение.
  • Ожидаемый результат: таблица заполнена, отмечено число красных флагов; три и больше — задача уже за границей, где bash дешевле.
  • Если не так: если ни один флаг не отметился и скрипт короткий — это нормальный ответ «оставить на bash», а не ошибка эксперимента.

Проверка результата. К концу занятия вы должны уметь ответить: во сколько раз дольше запускается Python и когда это важно; почему grep и awk быстрее и что из этого следует; как меняется соотношение объёма кода с ростом сложности задачи; какие три признака в вашем скрипте означают, что пора переписывать; чем count='0' после || true опасен.

5. Домашняя работа (1–2 часа)

Задание. Принять и обосновать решение по своей задаче, а затем проверить его на практике.

  1. Разбор своего скрипта. Возьмите свой bash-скрипт (или из блока 04: backup-reference.sh, log-report.sh, pods-report.sh подойдут) и заполните criteria-table.md целиком: признаки, подсчёт, измерения, красные флаги. Обязательно посчитайте накладные расходы на запуск за сутки исходя из реальной частоты вызовов.

  2. Карточка решения. Заполните decision-card.md по этой же задаче. Главные строки — «главный аргумент» и «при каком условии решение стоит пересмотреть»: без второй карточка бесполезна через полгода.

  3. Ядро задачи на двух языках. Выберите самую сложную часть своего скрипта (разбор данных, ветвление, обработка ошибок) и напишите её на обоих языках — по 20–40 строк. Посчитайте строки, сравните вывод командой diff, проверьте поведение на неожиданном входе.

  4. Переписывание по плану. Если по итогам пунктов 1–3 решение «переписать», заполните rewrite-plan.md и выполните шаги 1–5. Особое внимание шагу 2: прогоните старый скрипт по своим же проверкам до переписывания. Если решение «оставить как есть» — заполните раздел «что стало лучше» словами о том, почему переписывание не даст выигрыша.

  5. Проверка примеров линтерами. Прогоните свои новые файлы через те же инструменты, которыми проверены примеры урока:

    docker run --rm -v "$PWD:/mnt" koalaman/shellcheck:stable /mnt/ваш.sh
    /opt/tools/bin/ruff check ваш.py
    /opt/tools/bin/mypy --ignore-missing-imports ваш.py
    

    Замечания линтера не игнорируйте: в примере fetch_retry.py ruff нашёл перехват Exception вслепую (правило BLE001), и это было справедливо — исключения теперь перечислены явно.

Критерии приёмки.

  • criteria-table.md заполнена по конкретному скрипту, названо, какой признак перевесил
  • Измерено время запуска на своей машине и посчитаны накладные расходы за сутки
  • Пройден список красных флагов, указано число отмеченных
  • decision-card.md заполнена, включая условие пересмотра решения
  • Перечислены зависимости и указано, есть ли они на целевой машине
  • Ядро задачи написано на обоих языках, приведено число строк в каждой версии
  • Вывод обеих версий сравнён diff; при расхождении объяснено, какая версия права
  • Проверено поведение обеих версий на неожиданном входе, приведены сообщения и коды возврата
  • Если решение «переписать» — заполнен rewrite-plan.md и выполнен шаг 2 (прогон старого скрипта по проверкам до переписывания)
  • Если решение «оставить» — обосновано, почему переписывание не даст выигрыша
  • Новые файлы проходят shellcheck и ruff без замечаний

6. Частые ошибки и подводные камни

Переписывают из-за длины, а не из-за характера задачи. Симптом: скрипт из 80 строк переписан на Python и стал 200. Причина: длина сама по себе ничего не говорит — 80 строк последовательных вызовов утилит нормальны для bash. Что делать: смотреть, сколько логики между вызовами.

Считают, что Python быстрее. Симптом: переписали разбор лога и получили пятикратное замедление. Причина: grep и awk написаны на C и оптимизированы под построчную обработку. Что делать: измерять; выигрыш Python в поддержке, а не в скорости.

Сравнивают неаккуратно написанные версии. Симптом: «на Python вышло вдвое короче» при сравнении со скриптом, где на bash всё сделано через sed вручную. Причина: сравнивались не языки, а качество кода. Что делать: писать обе версии аккуратно, иначе сравнение бессмысленно.

Оставляют || true, чтобы set -e не мешал. Симптом: скрипт работает, ошибки не видно, данные испорчены. Причина: код возврата замаскирован, «ноль результатов» и «сбой» стали неразличимы. Что делать: разбирать код возврата явно; если это тяжело — признак, что пора переходить.

Тащат requests в скрипт, которому хватает стандартной библиотеки. Симптом: скрипт не запускается на сервере, потому что там нет пакета. Причина: удобство библиотеки перевесило вопрос запуска. Что делать: для простых запросов достаточно urllib.request; внешние зависимости берут осознанно, вместе с работой из урока 06.04.

Пытаются поставить пакет в системный Python. Симптом: error: externally-managed-environment. Причина: PEP 668 запрещает смешивать системные и свои пакеты. Что делать: виртуальное окружение (урок 06.04), а не --break-system-packages.

Переписывают и меняют интерфейс заодно. Симптом: переписывание превратилось в переделку всего, что вызывает скрипт. Причина: вместе с языком поменяли аргументы и формат вывода. Что делать: интерфейс фиксировать, менять его отдельным изменением потом.

Не проверяют старый скрипт до переписывания. Симптом: новая версия «не совпадает со старой», спорят, какая права. Причина: никто не знал, как старая ведёт себя на краевых случаях. Что делать: сначала записать проверки и прогнать старую версию, потом писать новую.

Смешивают в одном скрипте bash, jq и awk с логикой в каждом. Симптом: чтобы понять скрипт, надо знать три языка. Причина: логика расползлась по фильтрам. Что делать: считать это красным флагом, а не нормой.

Игнорируют замечания линтера в Python. Симптом: except Exception проглатывает опечатку в имени переменной, и скрипт «работает». Причина: перехват вслепую скрывает не те ошибки, которых ждали. Что делать: перечислять исключения явно; ruff это ловит правилом BLE001.

7. Проверка усвоения

  1. Во сколько раз дольше запускается Python и при каком условии это имеет значение?
  2. grep -c ERROR на файле из 20 000 строк быстрее Python-версии в восемь раз. Значит ли это, что переписывать на Python не надо?
  3. Как меняется соотношение объёма кода при переходе от простой задачи к задаче с повторами и разбором ошибок?
  4. Почему for f in $(ls *.log) дал пять элементов вместо трёх и как правильно?
  5. Что означает count='0' после count=$(f || true) и почему это опасно?
  6. Назовите три красных флага, после которых bash-скрипт стоит переписать.
  7. Какие модули для работы с данными есть в стандартной библиотеке Python, а какие придётся устанавливать?
  8. Что нельзя менять при переписывании скрипта на другой язык и почему?

8. Материалы

Минимум два варианта на выбор: официальная документация плюс книга/статья/видео.

  • Стандартная библиотека Python — то, что доступно без установки пакетов. Просмотрите оглавление целиком один раз: половина задач эксплуатации закрывается тем, что уже есть.
  • Учебник Python — официальный, короткий и без воды. Если Python вы не знаете, читать стоит главы 3–5 и 8 перед уроком 06.02.
  • Модуль json — пример того, как выглядит работа с форматом, когда она встроена в язык. Сравните с разбором того же JSON в bash.
  • Google Shell Style Guide — раздел «When to use Shell» отвечает на тот же вопрос, что этот урок, и отвечает жёстче: авторы предлагают переходить на другой язык уже после ста строк или при первом появлении сложных структур данных.
  • Wiki shellcheck — каталог того, чем bash опасен. Полезно как аргумент: каждое правило здесь появилось из настоящей ошибки.
  • PEP 20 «The Zen of Python» — двадцать строк, из которых для этого урока важна одна: «явное лучше неявного». Именно этим Python отличается от bash в обработке ошибок.

← Предыдущий: 05.14. Защищённый доступ к инфраструктуре: WireGuard, bastion · Блок 06 · Следующий: 06.02. Основы языка: типы, структуры данных, функции, исключения →

Материалы для практики

Скрипты, шпаргалки и заготовки к заданиям урока. Текст урока — на этой странице.