Bash-скрипт для начинающих: первый скрипт, переменные, условия, циклы и cron
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: с нуля до уверенного администрирования».
- Bash-скрипты: от команды в терминале до рабочего скрипта автоматизации
- Пакеты, окружение и первая автоматизация: apt, профиль и cron