Создание Bash-скрипта — полный практический разбор

16 марта 2026
Автор

Олег Марков

Введение

Bash-скрипт — это обычный текстовый файл с командами shell, который позволяет автоматизировать повторяющиеся задачи: деплой, бэкапы, миграции, проверку окружения, сбор логов и многое другое.

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

В этой статье я покажу, как правильно создавать Bash-скрипты с нуля, какие соглашения использовать и какие ошибки чаще всего допускают в реальных проектах.

Что такое Bash-скрипт

Bash-скрипт — файл (чаще всего с расширением .sh), который интерпретатор Bash выполняет построчно.

Простой пример:

echo "Привет, Bash"
date

Если сохранить эти строки в файл и выполнить его через Bash, команды отработают в указанном порядке.

Минимальная структура корректного скрипта

Рекомендованный шаблон старта:

#!/usr/bin/env bash
set -euo pipefail

main() {
  echo "Скрипт запущен"
}

main "$@"

Разберём по частям:

  • #!/usr/bin/env bash — shebang, указывает, чем запускать файл.
  • set -euo pipefail — «строгий режим» для безопасного поведения.
  • main() — точка входа для читаемой структуры.
  • main "$@" — передача всех аргументов скрипта в main.

Пошаговое создание первого скрипта

Шаг 1. Создать файл

touch backup.sh

Шаг 2. Добавить структуру

cat > backup.sh <<'EOF'
#!/usr/bin/env bash
set -euo pipefail

SOURCE_DIR="${1:-/var/log}"
TARGET_DIR="${2:-./backups}"
TIMESTAMP="$(date +%Y%m%d_%H%M%S)"
ARCHIVE_NAME="backup_${TIMESTAMP}.tar.gz"

main() {
  mkdir -p "$TARGET_DIR"
  tar -czf "$TARGET_DIR/$ARCHIVE_NAME" "$SOURCE_DIR"
  echo "Готово: $TARGET_DIR/$ARCHIVE_NAME"
}

main "$@"
EOF

Шаг 3. Выдать права на исполнение

chmod +x backup.sh

Шаг 4. Запустить

./backup.sh /var/log ./artifacts

Почему важен строгий режим

Конструкция:

set -euo pipefail

означает:

  • -e — завершать скрипт при ошибке команды (ненулевой exit code);
  • -u — падать при использовании необъявленной переменной;
  • -o pipefail — возвращать ошибку, если любой этап в pipeline завершился неуспешно.

Без этих опций скрипты часто «молча» продолжают работу после ошибок, что приводит к повреждённым артефактам или частично выполненным операциям.

Базовые правила структуры

1) Разделяй константы и логику

Сначала переменные/конфиг, затем функции, затем main.

2) Всегда экранируй переменные в кавычках

# плохо
cp $SOURCE_DIR/file.txt $TARGET_DIR/

# хорошо
cp "$SOURCE_DIR/file.txt" "$TARGET_DIR/"

Это защищает от пробелов и спецсимволов в путях.

3) Используй функции

Даже для среднего скрипта лучше разнести шаги по функциям:

check_dependencies() { ... }
prepare_dirs() { ... }
run_backup() { ... }

Так проще тестировать и дорабатывать.

4) Пиши явные сообщения об ошибках

if [[ ! -d "$SOURCE_DIR" ]]; then
  echo "Ошибка: директория не найдена: $SOURCE_DIR" >&2
  exit 1
fi

Пример production-friendly скрипта

#!/usr/bin/env bash
set -euo pipefail

readonly SCRIPT_NAME="$(basename "$0")"
readonly DEFAULT_SOURCE="/var/log"
readonly DEFAULT_TARGET="./backups"

log() {
  echo "[$SCRIPT_NAME] $*"
}

fail() {
  echo "[$SCRIPT_NAME][ERROR] $*" >&2
  exit 1
}

check_dependencies() {
  command -v tar >/dev/null 2>&1 || fail "Команда tar не найдена"
  command -v date >/dev/null 2>&1 || fail "Команда date не найдена"
}

parse_args() {
  SOURCE_DIR="${1:-$DEFAULT_SOURCE}"
  TARGET_DIR="${2:-$DEFAULT_TARGET}"
}

validate_input() {
  [[ -d "$SOURCE_DIR" ]] || fail "Директория не существует: $SOURCE_DIR"
}

run_backup() {
  local ts archive
  ts="$(date +%Y%m%d_%H%M%S)"
  archive="backup_${ts}.tar.gz"

  mkdir -p "$TARGET_DIR"
  tar -czf "$TARGET_DIR/$archive" "$SOURCE_DIR"
  log "Бэкап создан: $TARGET_DIR/$archive"
}

main() {
  check_dependencies
  parse_args "$@"
  validate_input
  run_backup
}

main "$@"

Частые ошибки при создании скриптов

Ошибка 1. Неправильный shebang

#!/bin/sh

Если ты используешь Bash-специфичные конструкции ([[ ]], массивы, source, mapfile), запускай именно Bash:

#!/usr/bin/env bash

Ошибка 2. Отсутствие прав на запуск

chmod +x script.sh

Без этого ./script.sh не выполнится.

Ошибка 3. Необработанные аргументы

Если скрипту нужны обязательные параметры, проверяй их явно:

[[ $# -ge 1 ]] || { echo "Использование: $0 <path>" >&2; exit 1; }

Ошибка 4. Игнорирование stderr

Сообщения об ошибках выводи в stderr (>&2), а не в обычный stdout — это упрощает логирование и пайпы.

Проверка и отладка нового скрипта

Быстрая проверка синтаксиса

bash -n script.sh

Трассировка выполнения

bash -x script.sh arg1 arg2

Линтинг

Рекомендуется использовать ShellCheck:

shellcheck script.sh

Он находит большинство типичных проблем до запуска в проде.

Рекомендации по стилю

  • Используй осмысленные имена переменных: SOURCE_DIR, TARGET_DIR, ARCHIVE_NAME.
  • Для неизменяемых значений применяй readonly.
  • Держи одну ответственность на функцию.
  • Добавляй краткие комментарии только там, где неочевидна причина решения.
  • Делай скрипт идемпотентным, если это возможно (повторный запуск не ломает состояние).

Итоги

Создание Bash-скрипта — это не просто файл с командами, а мини-программа с контрактом:

  • понятный вход (аргументы),
  • проверка предусловий,
  • предсказуемая обработка ошибок,
  • читаемая структура,
  • корректный код возврата.

Если с самого начала использовать shebang, строгий режим, функции и валидацию входных данных, скрипты становятся намного стабильнее и дешевле в поддержке.

Стрелочка влевоЗапуск Bash-скрипта — полный практический разборКомментарии в Bash — практический разборСтрелочка вправо

Все гайды по Bash

Редиректы и пайпы в Bash — полный разбор
Открыть базу знаний

Отправить комментарий