Bash-скрипты: автоматизация рутины
Если ты делаешь одно и то же в терминале больше двух раз - напиши скрипт. Деплой, бэкап, проверка здоровья сервиса - всё это автоматизируется за 10 минут.
Первый скрипт
#!/bin/bash
echo "Привет из скрипта!"
echo "Текущая дата: $(date)"
echo "Пользователь: $(whoami)"
chmod +x hello.sh
./hello.sh
Переменные
#!/bin/bash
APP_NAME="backend"
PORT=8080
LOG_DIR="/var/log/${APP_NAME}"
echo "Запуск ${APP_NAME} на порту ${PORT}"
echo "Логи: ${LOG_DIR}"
Правила:
- Без пробелов вокруг
=:NAME="value"(неNAME = "value") - Фигурные скобки
${VAR}- безопаснее, чем$VAR(особенно в строках) $()- подстановка команды:CURRENT_DATE=$(date +%Y-%m-%d)
Аргументы скрипта
#!/bin/bash
# Использование: ./deploy.sh production v1.2.3
ENVIRONMENT=$1 # первый аргумент
VERSION=$2 # второй аргумент
echo "Деплой версии ${VERSION} в ${ENVIRONMENT}"
| Переменная | Значение |
|---|---|
$0 | имя скрипта |
$1, $2, ... | аргументы |
$# | количество аргументов |
$@ | все аргументы |
$? | exit code предыдущей команды |
Условия
#!/bin/bash
if [ -z "$1" ]; then
echo "Ошибка: укажи окружение (production/staging)"
exit 1
fi
if [ "$1" = "production" ]; then
echo "Деплой в прод - будь осторожен!"
elif [ "$1" = "staging" ]; then
echo "Деплой в staging"
else
echo "Неизвестное окружение: $1"
exit 1
fi
Полезные проверки:
[ -f "file.txt" ] # файл существует
[ -d "directory" ] # каталог существует
[ -z "$VAR" ] # переменная пуста
[ -n "$VAR" ] # переменная не пуста
[ "$A" = "$B" ] # строки равны
[ "$A" -gt 10 ] # число больше 10
Циклы
#!/bin/bash
# Проверка здоровья нескольких сервисов
SERVICES=("backend:8080" "frontend:80" "postgres:5432")
for SERVICE in "${SERVICES[@]}"; do
HOST=$(echo "$SERVICE" | cut -d: -f1)
PORT=$(echo "$SERVICE" | cut -d: -f2)
if nc -z localhost "$PORT" 2>/dev/null; then
echo "[OK] ${HOST} - порт ${PORT} открыт"
else
echo "[FAIL] ${HOST} - порт ${PORT} не отвечает"
fi
done
Обработка ошибок
#!/bin/bash
set -euo pipefail
# set -e - остановиться при первой ошибке
# set -u - ошибка при использовании неопределённой переменной
# set -o pipefail - ошибка в пайпе не проглатывается
Практический пример: скрипт деплоя
#!/bin/bash
set -euo pipefail
APP_NAME="backend"
DEPLOY_DIR="/opt/${APP_NAME}"
BACKUP_DIR="${DEPLOY_DIR}/backups"
TIMESTAMP=$(date +%Y%m%d_%H%M%S)
echo "=== Деплой ${APP_NAME} ==="
# 1. Бэкап текущей версии
echo "Создаю бэкап..."
mkdir -p "${BACKUP_DIR}"
if [ -f "${DEPLOY_DIR}/${APP_NAME}" ]; then
cp "${DEPLOY_DIR}/${APP_NAME}" "${BACKUP_DIR}/${APP_NAME}_${TIMESTAMP}"
fi
# 2. Остановка сервиса
echo "Останавливаю сервис..."
sudo systemctl stop "${APP_NAME}" || true
# 3. Копирование нового бинарника
echo "Копирую новую версию..."
cp "./build/${APP_NAME}" "${DEPLOY_DIR}/${APP_NAME}"
chmod 755 "${DEPLOY_DIR}/${APP_NAME}"
# 4. Запуск
echo "Запускаю сервис..."
sudo systemctl start "${APP_NAME}"
# 5. Проверка
sleep 2
if curl -sf http://localhost:8080/health > /dev/null; then
echo "=== Деплой успешен ==="
else
echo "=== ОШИБКА: сервис не отвечает, откатываю ==="
cp "${BACKUP_DIR}/${APP_NAME}_${TIMESTAMP}" "${DEPLOY_DIR}/${APP_NAME}"
sudo systemctl start "${APP_NAME}"
exit 1
fi
Полезные паттерны
# Логирование
log() { echo "[$(date '+%H:%M:%S')] $*"; }
log "Начинаю деплой"
# Проверка зависимостей
command -v docker >/dev/null 2>&1 || { echo "Docker не установлен"; exit 1; }
# Cleanup при выходе
cleanup() { rm -f /tmp/myapp.lock; }
trap cleanup EXIT
Мини-задание
- Напиши скрипт
check-health.sh, который делаетcurlнаlocalhost:8080/healthи выводит OK/FAIL - Добавь
set -euo pipefailв начало - Добавь аргумент для порта:
./check-health.sh 3000 - Сделай
chmod +xи запусти
Итог
set -euo pipefail- первая строка после shebang. Без обсуждений.- Переменные в
${}, подстановка команд в$(). Пробелы в[ ]- обязательны. - Если делаешь что-то руками больше двух раз - напиши скрипт.
Типичная ошибка
Писать скрипт без set -e и без проверки ошибок. Скрипт «работает» - пока не ломается на середине и не оставляет систему в непонятном состоянии. Всегда думай: «что если эта строка упадёт?»
Мини-практика
Напиши скрипт system-info.sh, который выводит: 1) имя хоста, 2) IP-адрес, 3) uptime, 4) использование диска (df -h /), 5) топ-3 процесса по CPU. Используй переменные и форматированный вывод. Добавь set -euo pipefail. Запусти и убедись, что всё работает.