Урок по треку Linux 8 мин

Bash-скрипты без ловушек за 8 минут

Переменные и кавычки, условия, циклы, функции, set -euo pipefail, trap и частые ловушки.

Bash-скрипт это последовательность команд плюс немного логики. Основные ошибки в нём не синтаксические, а в кавычках, кодах возврата и пробелах в именах. Об этом и спрашивают.

Начало скрипта

#!/usr/bin/env bash
set -euo pipefail
  • -e: выйти, если команда вернула не 0 (с исключениями: условия if, while, части && и ||).
  • -u: обращение к неустановленной переменной это ошибка, а не пустая строка. Спасает от rm -rf "$DIR/"* с пустым DIR.
  • -o pipefail: конвейер падает, если упала любая его часть.

set -e не панацея: внутри функции, вызванной в if f; then, он отключён, а local x=$(cmd) маскирует ошибку cmd кодом local. Ошибки всё равно надо проверять явно там, где это важно.

Переменные и кавычки

name="world"          # без пробелов вокруг =
echo "Hello, $name"   # подстановка в двойных кавычках
echo 'Hello, $name'   # одинарные: всё буквально
files=$(ls)           # подстановка команды
echo "${name}s"       # фигурные скобки отделяют имя

Правило: всегда бери переменные в двойные кавычки. Без кавычек значение проходит разбиение по словам и раскрытие масок:

f="my report.txt"
rm $f     # rm "my" "report.txt"
rm "$f"   # правильно

Полезные подстановки: ${var:-default} (значение по умолчанию), ${var:?сообщение} (упасть, если пусто), ${#var} (длина), ${file%.log} (срезать суффикс), ${path##*/} (имя файла из пути), ${s/old/new} (замена).

Аргументы

$0 имя скрипта, $1...$9 аргументы, $# их число, "$@" все аргументы по отдельности, $? код последней команды, $$ PID скрипта. "$@" в кавычках сохраняет аргументы с пробелами, $* склеивает их.

Условия

if [[ -f "$cfg" && -r "$cfg" ]]; then ...; fi
if [[ "$env" == prod* ]]; then ...; fi     # маска
if [[ "$v" =~ ^[0-9]+$ ]]; then ...; fi    # регулярка
if (( count > 10 )); then ...; fi          # арифметика
if grep -q ERROR app.log; then ...; fi     # код возврата команды

[[ ]] это конструкция bash: внутри нет разбиения по словам, работают &&, маски и =~. [ ] это команда test из POSIX, там переменные обязательно в кавычках. Проверки файлов: -f обычный файл, -d каталог, -e существует, -s непустой, -x исполняемый. Строки: -z пустая, -n непустая.

Циклы

for f in *.log; do gzip "$f"; done
for i in {1..5}; do echo "$i"; done
while IFS= read -r line; do echo "$line"; done < hosts.txt
for (( i = 0; i < 3; i++ )); do echo "$i"; done

for line in $(cat file) это ошибка: файл режется по пробелам, а не по строкам, и маски раскрываются. Для строк есть только while read.

Функции

log() { echo "[$(date +%T)] $*" >&2; }
check_port() {
  local host=$1 port=$2
  timeout 2 bash -c "</dev/tcp/$host/$port" 2>/dev/null
}
if check_port db 5432; then log "db up"; fi

Функция возвращает код (return 0..255), а данные отдаёт через stdout. Переменные по умолчанию глобальные, внутри функции пиши local.

Массивы

hosts=(web1 web2 "db 1")
for h in "${hosts[@]}"; do echo "$h"; done
echo "${#hosts[@]}"
declare -A port=([web]=80 [db]=5432)
echo "${port[db]}"

trap: уборка за собой

tmp=$(mktemp -d)
trap 'rm -rf "$tmp"' EXIT

Обработчик EXIT выполнится при любом выходе, в том числе по set -e. trap '...' INT TERM перехватывает сигналы. Временные файлы создают через mktemp, а не /tmp/myfile: фиксированное имя это гонка и дыра в безопасности.

Отладка и проверка

  • bash -n script.sh проверяет синтаксис без запуска.
  • bash -x script.sh или set -x печатает каждую команду перед выполнением.
  • shellcheck ловит почти все ловушки из этого урока. В CI его стоит запускать всегда.

Частые ошибки

  • Переменные без кавычек.
  • for x in $(cat file) для чтения строк.
  • cd dir; rm -rf * без проверки, что cd удался. Правильно cd dir || exit 1.
  • Пробелы вокруг = в присваивании: a = 1 это запуск команды a.
  • Надежда на set -e как на полную защиту.
  • Сравнение чисел через == в [ ] или строк через -eq.

Что спросят на собесе

  • Что делает set -euo pipefail и где -e не срабатывает.
  • Разница "$@" и $*, одинарных и двойных кавычек.
  • Чем [[ ]] отличается от [ ].
  • Как прочитать файл построчно и почему не через for.
  • Как гарантированно удалить временный файл при выходе.

Закрепите за 5 минут

После урока в тренажёре 3-5 проверочных вопросов, дальше модуль уходит в повторения.