Инструкция

Bash-скрипт для начинающих: первый скрипт, переменные, условия, циклы и cron

Обновлено: · Skillaria.Top

Bash-скрипт — это текстовый файл с командами оболочки, который начинается со строки #!/bin/bash, получает право на запуск через chmod +x и запускается как ./script.sh. В скриптах используют переменные, аргументы $1 и $2, условия if, циклы for и while, а регулярный запуск по расписанию настраивают через crontab -e.

Bash-скрипт — это обычный текстовый файл с командами, которые вы иначе набирали бы в терминале по одной. Скрипт начинается со строки shebang #!/bin/bash, получает право на выполнение через chmod +x и запускается как ./script.sh. Это руководство — bash скрипт для начинающих от первой строки до запуска по расписанию: переменные, аргументы, условия, циклы, обработка ошибок и cron.

Первый bash-скрипт за минуту

Создайте файл в любом редакторе, например в nano:

nano hello.sh
#!/bin/bash
# Первый скрипт: приветствие и немного информации о системе
echo "Привет, $USER!"
echo "Сегодня: $(date +%Y-%m-%d)"
echo "Вы находитесь в: $(pwd)"

Сохраните (Ctrl+O, Enter, Ctrl+X), дайте право на запуск и запустите:

$ chmod +x hello.sh
$ ./hello.sh
Привет, alice!
Сегодня: 2026-10-12
Вы находитесь в: /home/alice

Строки, начинающиеся с #, — комментарии. $(команда) подставляет вывод команды прямо в строку.

Shebang: зачем нужна первая строка

Строка #!/bin/bash говорит системе, каким интерпретатором выполнять файл. Без неё скрипт запустится той оболочкой, что окажется под рукой, а в некоторых системах /bin/sh — это не bash, а более простой dash, где часть конструкций не работает.

Часто встречается и вариант #!/usr/bin/env bash: он ищет bash в PATH, что удобно, если bash установлен не в /bin.

Как запустить скрипт и почему ./

Есть два способа:

  • ./hello.sh — нужен shebang и право x. Префикс ./ обязателен, потому что текущий каталог не входит в PATH.
  • bash hello.sh — право на запуск не нужно, интерпретатор указан явно.

Если без ./ вы видите command not found, а без chmod +x — Permission denied, это ровно эти две причины. Чтобы вызывать свои скрипты по имени из любого места, положите их в ~/bin или /usr/local/bin.

Переменные

Переменная задаётся без пробелов вокруг =, а читается через $:

#!/bin/bash
name="backup"
dir="/var/backups"
today=$(date +%F)

echo "Файл: ${dir}/${name}-${today}.tar.gz"

Фигурные скобки отделяют имя переменной от окружающего текста. Строка name = "backup" с пробелами — ошибка: bash попытается запустить команду name.

Кавычки имеют значение

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

file="мой отчёт.txt"
rm $file      # ошибка: попытается удалить «мой» и «отчёт.txt»
rm "$file"    # правильно
echo '$HOME'  # выведет буквально $HOME
echo "$HOME"  # выведет /home/alice

Переменные окружения

Переменные вроде $HOME, $USER, $PATH задаёт система. Свою переменную можно передать дочерним процессам через export. Постоянные настройки окружения кладут в ~/.bashrc или ~/.profile.

Аргументы скрипта

Значения, переданные при запуске, доступны как $1, $2 и так далее.

ПеременнаяЧто содержит
$0имя скрипта
$1, $2…первый, второй аргумент
$#число аргументов
"$@"все аргументы по отдельности
$?код завершения последней команды
$$PID текущего скрипта
#!/bin/bash
echo "Скрипт $0 получил $# аргумента(ов)"
echo "Первый: $1"
$ ./args.sh nginx 80
Скрипт ./args.sh получил 2 аргумента(ов)
Первый: nginx

Ввод от пользователя читается командой read: read -p "Введите имя: " name.

Условия if

Каждая команда возвращает код завершения: 0 — успех, всё остальное — ошибка. Конструкция if проверяет именно его. Для сравнений используют [[ ... ]], пробелы внутри скобок обязательны.

#!/bin/bash
if [[ $# -eq 0 ]]; then
    echo "Использование: $0 <каталог>"
    exit 1
fi

if [[ -d "$1" ]]; then
    echo "Каталог $1 существует"
elif [[ -f "$1" ]]; then
    echo "$1 — это файл, а не каталог"
else
    echo "$1 не найден"
fi
ПроверкаЗначение
-f fileобычный файл существует
-d dirкаталог существует
-e pathпуть существует
-x fileфайл можно выполнить
-z "$var" / -n "$var"строка пустая / непустая
"$a" == "$b", !=сравнение строк
-eq -ne -lt -le -gt -geсравнение чисел

Проверять можно и любую команду напрямую:

if systemctl is-active --quiet nginx; then
    echo "nginx работает"
else
    echo "nginx остановлен"
fi

Для множества вариантов удобен case:

case "$1" in
    start)   echo "Запуск" ;;
    stop)    echo "Остановка" ;;
    *)       echo "Неизвестная команда: $1"; exit 1 ;;
esac

Циклы for и while

for: перебрать список

for host in web1 web2 db1; do
    echo "Проверяю $host"
    ping -c 1 -W 1 "$host" > /dev/null && echo "  доступен" || echo "  недоступен"
done

for file in /var/log/*.log; do
    echo "$file: $(wc -l < "$file") строк"
done

for i in {1..5}; do
    echo "Попытка $i"
done

while: пока условие верно

Классический пример — построчное чтение файла:

while read -r user; do
    echo "Пользователь: $user"
done < users.txt

И ожидание, пока сервис поднимется:

count=0
until curl -sf http://localhost:8080/health > /dev/null; do
    count=$((count + 1))
    if [[ $count -ge 10 ]]; then
        echo "Сервис не поднялся"
        exit 1
    fi
    sleep 3
done
echo "Сервис готов"

Арифметика в bash записывается в двойных скобках: $((a + b)). Работает она только с целыми числами.

Функции

Повторяющийся код выносят в функции. Аргументы внутри функции — те же $1, $2, а local не даёт переменной вылезти наружу.

log() {
    local level="$1"; shift
    echo "$(date '+%F %T') [$level] $*"
}

log INFO "Начинаю резервное копирование"
log ERROR "Нет места на диске"

Обработка ошибок: set -euo pipefail

По умолчанию bash продолжает выполнение после упавшей команды — и скрипт резервного копирования может «успешно» завершиться, ничего не скопировав. В начало скриптов принято добавлять строгий режим:

#!/bin/bash
set -euo pipefail
  • -e — остановиться при первой ошибке.
  • -u — считать ошибкой обращение к необъявленной переменной (защищает от rm -rf "$DIR/" с пустым DIR).
  • -o pipefail — конвейер считается упавшим, если упала любая его часть.

Для отладки запустите скрипт как bash -x script.sh: bash будет печатать каждую команду перед выполнением. Найти типичные ошибки до запуска поможет утилита shellcheck.

Практический пример: скрипт резервного копирования

#!/bin/bash
set -euo pipefail

SRC="${1:-/var/www}"
DST="/var/backups/site"
KEEP_DAYS=7
STAMP=$(date +%F_%H-%M)

mkdir -p "$DST"
tar -czf "$DST/site-$STAMP.tar.gz" -C "$(dirname "$SRC")" "$(basename "$SRC")"
echo "Создан архив $DST/site-$STAMP.tar.gz"

# удалить архивы старше KEEP_DAYS дней
find "$DST" -name "site-*.tar.gz" -mtime +"$KEEP_DAYS" -delete

Запись ${1:-/var/www} означает «первый аргумент, а если его нет — /var/www». Сохраните скрипт как /usr/local/bin/backup-site.sh и сделайте исполняемым: sudo chmod +x /usr/local/bin/backup-site.sh.

Запуск по расписанию: cron

Cron — служба, которая запускает команды по расписанию. Задания пользователя редактируются командой crontab -e, просмотреть их можно через crontab -l, задания root — через sudo crontab -e.

Строка задания состоит из пяти полей времени и команды:

# минута час день_месяца месяц день_недели команда
30 2 * * * /usr/local/bin/backup-site.sh >> /var/log/backup.log 2>&1
РасписаниеКогда запускается
*/5 * * * *каждые 5 минут
0 * * * *в начале каждого часа
30 2 * * *каждый день в 02:30
0 9 * * 1-5по будням в 09:00
0 3 1 * *1-го числа каждого месяца в 03:00
@rebootодин раз после загрузки

Почему скрипт работает руками, но не в cron

  • Другое окружение. Cron запускает задания с минимальным PATH и без вашего ~/.bashrc. Пишите полные пути к командам и файлам или задайте PATH в начале crontab.
  • Другой текущий каталог. Задание стартует в домашнем каталоге, а не там, где лежит скрипт. Используйте абсолютные пути.
  • Символ %. В crontab знак % означает перевод строки, поэтому date +%F прямо в строке задания нужно экранировать как \%. Проще вынести команду в скрипт.
  • Вывод теряется. Перенаправляйте вывод в лог (>> file 2>&1), иначе ошибку не увидеть. Запуски cron видны в journalctl -u cron (в RHEL-подобных системах служба называется crond).

На системах с systemd альтернативой cron служат таймеры systemd: они пишут вывод в журнал и умеют догонять пропущенные запуски.

Итог

Bash скрипт для начинающих — это файл с shebang #!/bin/bash, правом на запуск и последовательностью команд. Добавьте переменные в кавычках, проверку аргументов через if, циклы for и while, строгий режим set -euo pipefail — и получите надёжный инструмент автоматизации, который cron будет запускать по расписанию. Bash-скрипты, окружение и cron разобраны на практике в курсе по Linux.

Частые вопросы

Чем bash отличается от sh?

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

Какое расширение должно быть у bash-скрипта?

Linux не смотрит на расширение: интерпретатор определяет shebang. Расширение .sh — просто соглашение, чтобы человеку было понятно, что это скрипт.

Как передать в скрипт пароль безопасно?

Не передавайте его аргументом — аргументы видны другим пользователям в ps. Используйте переменную окружения из файла с правами 600 или read -s для ввода без отображения.

Как проверить, что скрипт запущен от root?

Сравните идентификатор пользователя с нулём: if [[ $EUID -ne 0 ]]; then echo "Нужен root"; exit 1; fi.

Как запустить скрипт в фоне, чтобы он не остановился после выхода из SSH?

Используйте nohup ./script.sh &, сессию tmux или screen, а для постоянной работы оформите скрипт как службу systemd.

Разберите тему на видео

Эта тема подробно разобрана с практикой в курсе «Linux: с нуля до уверенного администрирования».

Перейти к курсу