Bash-скрипты, которые не падают: 5 привычек админа
Почти у каждого админа есть история про скрипт, который «в проде» удалил не тот каталог или молча пропустил ошибку. Большинство таких историй предотвращается пятью привычками, которые занимают две минуты на скрипт.
1. set -euo pipefail — страховка по умолчанию
Первая строка после shebang:
#!/usr/bin/env bash
set -euo pipefail
-e— выход при первой упавшей команде;-u— ошибка при обращении к необъявленной переменной (спасает отrm -rf $DIR/с пустым$DIR);pipefail— конвейер падает, если упал любой его участник, а не только последний.
Без pipefail конструкция mysqldump db | gzip > dump.gz радостно создаст пустой архив даже при упавшем dump — классика жанра.
2. Кавычки вокруг переменных. Всегда
cp "$source" "$destination"
for f in "$dir"/*; do ...; done
Без кавычек bash расколет значение по пробелам — и файл my report.pdf станет двумя аргументами. Единственное осознанное исключение — когда word splitting нужен намеренно, и тогда это стоит прокомментировать.
3. trap для уборки за собой
Скрипт, создающий временные файлы, обязан убрать их даже при падении посередине:
tmpdir=$(mktemp -d)
trap 'rm -rf "$tmpdir"' EXIT
trap ... EXIT срабатывает при любом завершении: успех, ошибка, Ctrl+C. Особенно ценно в скриптах, которые монтируют диски, блокируют ресурсы или оставляют lock-файлы.
4. [[ ]] вместо [ ]
if [[ -f "$config" && "$mode" == "prod" ]]; then
echo "careful!"
fi
Двойные скобки — встроенная конструкция bash, а не внешняя команда: внутри не работает word splitting (кавычки не критичны), доступны &&, || и сравнение строк по шаблону == *.log. Меньше синтаксических граблей на ровном месте.
5. "$@" вместо $* в функциях
Передавая аргументы дальше, пишите "$@" в кавычках:
log_and_run() {
echo "[$(date -Is)] $*" >> /var/log/runner.log
"$@"
}
"$@" сохраняет каждый аргумент отдельным словом — аргумент с пробелами доедет до цели целым. $* склеит всё в одну строку. Для вывода в лог склейка уместна, для исполнения — нет, и разница видна на примере выше.
Бонус: shellcheck
apt install shellcheck
shellcheck backup.sh
Shellcheck ловит всё перечисленное автоматически и ещё полтысячи типовых ошибок. Есть расширения под любой редактор. Делать его запуск частью привычки «написал — проверил» — самый дешёвый ревьюер в команде.
Хороший bash-скрипт — это не тот, где «умно» решена задача, а тот, который при ошибке громко упал рядом с местом проблемы. Тихий скрипт с неверным результатом страшнее любого падения.
Пять привычек не делают из bash Python и не отменяют здравого смысла — но скрипт, написанный с ними, можно спокойно оставить в cron и уйти в отпуск.