Основы bash в ОС Linux: создание скриптов, команды, ошибки

Практически любая рутинная задача на Linux-сервере – резервное копирование, работа с логами, перезапуск сервисов, обработка файлов – может выполняться автоматически. Инструмент, который это позволяет, называется Bash-скрипт. Это обычный текстовый файл со списком команд, которые система выполняет одну за другой.

В этой статье разберем основы Bash: что это за оболочка, как устроен синтаксис скриптов, как работать с переменными, условиями, циклами и функциями, а также как запускать скрипты на сервере. Все примеры можно повторить на любом Linux-сервере с доступом по SSH – например, на VPS от Beget.

Что такое Bash

Bash (Bourne Again Shell) – это командная оболочка (shell), то есть программа-посредник между пользователем и ядром операционной системы. Вы вводите текстовую команду, Bash ее разбирает, запускает нужную программу и показывает результат.

Bash появился в 1989 году как развитие более старой оболочки sh (Bourne Shell) и с тех пор он установлен по умолчанию в большинстве дистрибутивов Linux: Ubuntu, Debian, CentOS, AlmaLinux и других. Когда вы подключаетесь к серверу по SSH и видите приглашение вида user@server:~$, это именно командная оболочка Bash.

Оболочка работает в двух режимах:

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

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

Проверить, какая оболочка используется у вас, можно командой:

echo $0

А узнать версию Bash – так:

bash --version

Чем Bash отличается от sh, zsh и других оболочек

В Linux существует несколько оболочек, и путаница между ними – источник трудноуловимых ошибок в скриптах. Разберем самые популярные: 

  • sh – минималистичная POSIX-оболочка. Скрипты максимально переносимы, но многих удобных возможностей нет. В Debian и Ubuntu /bin/sh – это симлинк на dash, а не на bash;
  • bash – в основном совместим с POSIX-sh, но добавляет много собственных возможностей. Скрипты, написанные с учетом этого стандарта, будут исполняться без изменений в любых системах, поддерживающих POSIX;
  • zsh – мощная и гибкая оболочка, поддерживающая темы и плагины. Используется по умолчанию в macOS. Полной POSIX-совместимости у zsh нет, хотя большинство Bash-скриптов выполняются без изменений. При необходимости zsh может запускаться в режиме sh, а синтаксис во многом пересекается с bash;
  • fish – в отличие от bash и zsh, изначально создавалась как оболочка, ориентированная на удобство, но не совместимая с POSIX. Поэтому скрипты, написанные для bash или zsh, как правило, не будут исполняться, их нужно адаптировать под fish.
Обратите внимание!
Если в первой строке скрипта указан #!/bin/bash, запускайте его командой bash script.sh или ./script.sh, но не sh script.sh – во втором случае скрипт выполнит другая оболочка, что может повлечь ошибки.

Для чего используют Bash-скрипты

Bash-скрипты применяют везде, где нужна автоматизация на уровне операционной системы:

  • резервное копирование. Скрипт архивирует каталог с сайтом и дамп базы данных, складывает архив в отдельную папку и удаляет копии старше 30 дней;
  • обслуживание сервера. Очистка временных файлов и логов, ротация архивов, контроль свободного места на диске;
  • мониторинг. Проверка доступности сайта, состояния служб, нагрузки на CPU и память с отправкой уведомления при отклонении от нормы;
  • развертывание проектов. Установка зависимостей, сборка приложения, миграции базы данных, перезапуск сервисов – все одной командой;
  • массовая обработка файлов. Переименование, конвертация, поиск и замена текста в сотнях файлов;
  • настройка окружения. Файлы .bashrc и .bash_profile позволяют задать переменные окружения, псевдонимы (алиасы) и вид приглашения командной строки;
  • связывание утилит через конвейеры. bash передает вывод одной программы на вход другой, что позволяет собирать сложные обработки данных из простых команд.

Основные команды Bash

Основные команды Bash
Основные команды Bash

Синтаксис Bash-скриптов

Bash-скрипт – это обычный текстовый файл. Создать его можно в любом редакторе:

  • nano – самый простой вариант для новичка: подсказки по горячим клавишам всегда внизу экрана, сохранение сочетанием клавиш Ctrl+O и выход по Ctrl+X;
  • vim – мощный редактор с модальным управлением, подсветкой синтаксиса и макросами, но требует времени на освоение.

Расширение .sh для скриптов не обязательно, но общепринято – оно помогает быстро понять назначение файла.

Шебанг

Первая строка скрипта называется шебанг (shebang) и указывает системе, какой интерпретатор использовать. Она начинается с символов #!, за которыми следует путь к интерпретатору:

#!/bin/bash

Также можно явно указать путь к интерпретатору. Может быть полезна, если на разных системах bash лежит в разных каталогах:

#!/usr/bin/env bash

Комментарии

Всё, что идет после символа # (кроме шебанга), интерпретатор игнорирует. Комментарии описывают логику скрипта и не влияют на выполнение:

#!/bin/bash
# Этот скрипт выводит приветствие
echo "Привет!"   # комментарий можно писать и в конце строки

Кавычки

Кавычки – то место, где чаще всего можно допустить ошибку:

  • "текст $var" – двойные кавычки: переменные и команды подставляются;
  • 'текст $var' – одинарные кавычки: всё воспринимается буквально, подстановки нет;
  • `команда` или `$(команда)` – подстановка результата выполнения команды.

Пример:

#!/bin/bash
name="Beget"
echo "Привет, $name"      # Привет, Beget
echo 'Привет, $name'      # Привет, $name
echo "Дата: $(date +%F)"  # Дата: 2026-07-24
Обратите внимание!
Всегда заключайте переменные в двойные кавычки – "$file", а не `$file` – иначе имя файла с пробелом bash разделит на несколько аргументов, и скрипт сработает не так, как задумано.

Как создать и запустить Bash-скрипт

  1. В первую очередь необходимо создать файл: 
touch hello.sh
  1. Откройте файл любым текстовым редактором, впишите код и сохраните файл:
#!/bin/bash
echo "Hello, World!"
  1. Сделайте файл исполняемым. По умолчанию новый файл не имеет права на выполнение. Его нужно выдать командой chmod:
chmod +x hello.sh

Общий синтаксис команды:

chmod [опции] права файл

Права можно задавать двумя способами: 

  • символьным (+x, u+rw
  • числовым (700, 644)

Несколько примеров: 

  • 755rwxr-xr-x – владелец может всё, остальные – читать и выполнять
  • 700rwx------ – доступ только владельцу
  • 644rw-r--r-- – обычный файл без права на выполнение
  1. Запуск скрипта. Есть три способа:
  • ./hello.sh – запуск как программы, нужен бит выполнения и шебанг
  • bash hello.sh – явный запуск через интерпретатор, бит выполнения не нужен
  • source hello.sh – выполнение в текущей оболочке (или короткая форма: . hello.sh)

При запуске с помощью source скрипт выполняется не в отдельном процессе, а в текущей сессии, поэтому заданные в нем переменные останутся доступны после завершения. Именно так подключают файлы конфигурации.

Обратите внимание!
В первом варианте без ./ bash будет искать hello.sh только в каталогах из переменной PATH и выдаст ошибку command not found.

Переменные

Переменные хранят данные: строки, числа, результаты выполнения команд.

Создание и использование

Переменная создается оператором присваивания =

Обратите внимание!
Пробелы вокруг знака = недопустимы.
#!/bin/bash
host="beget.com"     # правильно
# host = "beget.com" # ошибка: command not found

Чтобы получить значение, перед именем ставится символ $. Форма с фигурными скобками ${host} нужна, когда сразу после имени идет другой текст:

echo "$host"
echo "${host}_backup"   # без скобок Bash искал бы переменную host_backup

Ввод данных от пользователя

Команда read считывает строку с клавиатуры и записывает ее в переменную:

#!/bin/bash
read -p "Введите ваше имя: " username
echo "Здравствуйте, $username!"

Параметр -p выводит приглашение, а -s скрывает ввод – это удобно при запросе пароля.

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

Часть переменных система задает автоматически. Полный список выводит команда env:

  • $HOME – домашний каталог пользователя;
  • $USER – имя текущего пользователя;
  • $PWD – текущий рабочий каталог;
  • $PATH – список каталогов, где ищутся исполняемые файлы;
  • $SHELL – путь к текущей оболочке;
  • $HOSTNAME – имя хоста.

Чтобы переменная стала доступна дочерним процессам, ее экспортируют:

export API_KEY="secret"

Арифметика

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

#!/bin/bash
a=10
b=3
echo $(( a + b ))   # 13
echo $(( a * b ))   # 30
echo $(( a / b ))   # 3 - целочисленное деление
echo $(( a % b ))   # 1 - остаток

Для дробных чисел встроенных средств нет, используется утилита bc:

echo "scale=2; 10 / 3" | bc   # 3.33

Параметры командной строки

Скрипту можно передавать аргументы при запуске – внутри они доступны через позиционные переменные $1, $2, $3 и так далее.

#!/bin/bash
echo "Привет, $1! Тебе $2 лет."

Запуск:

./greet.sh Иван 30
# Привет, Иван! Тебе 30 лет.

Помимо позиционных, есть специальные переменные:

  • $0 – имя самого скрипта;
  • $# – количество переданных аргументов;
  • $@ – все аргументы как отдельные элементы (используйте "$@");
  • $* – все аргументы одной строкой;
  • $? – код возврата последней команды: 0 – успех, любое другое число – ошибка;
  • $$ – PID текущего процесса.

Условные операторы

Условия позволяют скрипту принимать решения.

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

if [ условие ]; then
    команды
elif [ другое_условие ]; then
    команды
else
    команды
fi

Обратите внимание на пробелы внутри квадратных скобок – [ $a -eq $b ]. Без них bash выдаст ошибку, потому что [ – это не синтаксический элемент, а самостоятельная команда.

Сравнение чисел

  • -eq – равно;
  • -ne – не равно;
  • -gt – больше;
  • -ge – больше или равно;
  • -lt – меньше;
  • -le – меньше или равно.

Сравнение строк

  • = или == – строки равны;
  • != – строки не равны;
  • -z – строка пустая;
  • -n – строка не пустая.

Проверка файлов

  • -e – файл или каталог существует;
  • -f – существует и является обычным файлом;
  • -d – существует и является каталогом;
  • -r / -w / -x – доступен для чтения / записи / выполнения;
  • -s – существует и не пустой.

Пример:

#!/bin/bash
file="/var/log/nginx/error.log"
if [ -f "$file" ]; then
    echo "Файл найден, размер: $(du -h "$file" | cut -f1)"
else
    echo "Файл $file не найден"
    exit 1
fi

Логические операторы

Условия комбинируются через && (И), || (ИЛИ) и ! (отрицание):

#!/bin/bash
age=25
if [ "$age" -ge 18 ] && [ "$age" -lt 65 ]; then
    echo "Возраст в рабочем диапазоне"
fi
if [ ! -d "/backup" ]; then
    mkdir -p /backup
fi

В Bash вместо одинарных скобок удобнее использовать двойные [[ ]]: они безопаснее при работе с переменными, поддерживают шаблоны и логику &&/|| внутри скобок:

if [[ "$filename" == *.txt && -f "$filename" ]]; then
    echo "Это текстовый файл"
fi

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

Когда вариантов много, case читается лучше длинной цепочки elif:

#!/bin/bash
case "$1" in
    start)
        echo "Запуск сервиса"
        ;;
    stop)
        echo "Остановка сервиса"
        ;;
    restart)
        echo "Перезапуск сервиса"
        ;;
    *)
        echo "Использование: $0 {start|stop|restart}"
        exit 1
        ;;
esac

Циклы

Циклы выполняют один и тот же блок команд многократно.

Цикл for

Перебирает элементы списка:

#!/bin/bash
for number in 1 2 3 4 5; do
    echo "Итерация $number"
done

Диапазон удобнее записывать через фигурные скобки:

for i in {1..10}; do
    echo "$i"
done

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

#!/bin/bash

for file in /var/log/*.log; do
    echo "Обрабатываю: $file"
    gzip "$file"
done

Цикл while

Выполняется, пока условие остается истинным:

#!/bin/bash
counter=1
while [ "$counter" -le 5 ]; do
    echo "Счетчик: $counter"
    counter=$(( counter + 1 ))
done

Типичный прием – построчное чтение файла:

#!/bin/bash
while read -r line; do
    echo "Строка: $line"
done < /etc/hosts

Цикл until

Противоположность while – выполняется, пока условие ложно, и останавливается, когда оно станет истинным.

#!/bin/bash
count=1
until [ "$count" -gt 3 ]; do
    echo "Попытка $count"
    count=$(( count + 1 ))
done

break и continue

break досрочно выходит из цикла, continue пропускает текущую итерацию:

#!/bin/bash
for i in {1..10}; do
    if [ "$i" -eq 5 ]; then
        continue    # пропускаем пятерку
    fi
    if [ "$i" -gt 8 ]; then
        break       # выходим из цикла
    fi
    echo "$i"
done

Функции

Функции группируют команды в именованные блоки, которые можно вызывать многократно. Это избавляет от дублирования кода.

Объявление

Два равнозначных варианта:

my_function() {
    команды
}
# или
function my_function {
    команды
}

Функцию нужно объявить до первого вызова – bash читает скрипт сверху вниз.

Аргументы

Аргументы передаются через пробел после имени функции, без круглых скобок и запятых. Внутри они доступны как $1, $2 и так далее:

#!/bin/bash
greet() {
    echo "Привет, $1! Тебе $2 лет."
}

greet "Анна" 30

Локальные переменные и возврат значений

По умолчанию все переменные в bash глобальные. Чтобы переменная существовала только внутри функции, она объявляется с ключевым словом local – это защищает от неожиданной перезаписи данных:

#!/bin/bash
calculate() {
    local result=$(( $1 + $2 ))
    echo "$result"     # «возврат» значения через вывод
}
sum=$(calculate 5 7)
echo "Сумма: $sum"     # Сумма: 12

Оператор return в Bash возвращает код завершения (число от 0 до 255), а не значение. Чтобы вернуть данные, их выводят через echo и перехватывают подстановкой $( ):

#!/bin/bash
check_file() {
    if [ -f "$1" ]; then
        return 0    # успех
    else
        return 1    # ошибка
    fi
}
if check_file "/etc/passwd"; then
    echo "Файл существует"
fi

Массивы

Массив хранит набор значений под одним именем:

#!/bin/bash
servers=("web1" "web2" "db1")
echo "${servers[0]}"      # web1 - нумерация с нуля
echo "${servers[@]}"      # все элементы
echo "${#servers[@]}"     # количество элементов: 3
servers+=("cache1")       # добавить элемент
for server in "${servers[@]}"; do
    echo "Проверяю $server"
done

Работа с файлами и директориями

Проверить существование файла можно командой test или ее краткой формой – квадратными скобками:

test -e filename
# то же самое:
[ -e filename ]

Команда возвращает 0, если файл существует, и ненулевое значение в противном случае. Результат доступен в переменной $?.

Несколько типовых операций:

#!/bin/bash
# Создать каталог, если его нет
mkdir -p /backup/2026
# Скопировать с сохранением прав и меток времени
cp -a /var/www/site /backup/site
# Найти и удалить файлы старше 30 дней
find /backup -name "*.tar.gz" -mtime +30 -delete
# Посчитать количество строк в файле
lines=$(wc -l < /var/log/access.log)
echo "Строк в логе: $lines"
# Создать архив
tar -czf /backup/site-$(date +%Y-%m-%d).tar.gz /var/www/site

Перенаправление потоков и конвейеры

У каждой программы в Linux есть три стандартных потока: ввод (stdin, дескриптор 0), вывод (stdout, 1) и вывод ошибок (stderr, 2). Bash позволяет ими управлять:

  • команда > file – записать вывод в файл, перезаписав его;
  • команда >> file – записать вывод в конец файла;
  • команда 2> file – записать в файл только ошибки;
  • команда > file 2>&1 – записать в файл и вывод, и ошибки;
  • команда > /dev/null 2>&1 – полностью подавить любой вывод;
  • команда1 | команда2 – передать вывод первой команды на вход второй.

Конвейер (pipe) используются для связи нескольких команд путем перенаправления вывода одной команды (stdout) на вход (stdin) последующей, используя символ конвейера |.

# Топ-10 IP-адресов по количеству запросов в логе nginx
cat /var/log/nginx/access.log | awk '{print $1}' | sort | uniq -c | sort -rn | head -10

Запуск скриптов по расписанию через cron

Для запуска скрипта, который нужно выполнять регулярно, используется планировщик cron

crontab -e

Синтаксис для добавления задания через терминал:  минута час день месяц день_недели команда. Пример: 

# Каждый день в 03:30 запускать резервное копирование
30 3 * * * /home/user/scripts/backup.sh >> /home/user/logs/backup.log 2>&1

На виртуальном хостинге Beget задания cron удобнее настраивать через раздел CronTab в панели управления.

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

Пошаговый режим показывает каждую команду перед выполнением с подставленными значениями переменных:

bash -x script.sh

Внутри скрипта отладку можно включать и выключать для отдельного участка:

set -x    # включить трассировку
# проблемный код
set +x    # выключить

Еще один прием, который может быть полезен, – “строгий режим” в начале каждого скрипта:

#!/bin/bash
set -euo pipefail
  • -e – прервать выполнение при первой же ошибке;
  • -u – считать ошибкой обращение к необъявленной переменной;
  • -o pipefail – возвращать ошибку, если сбой произошел в любом звене конвейера.

Без этих настроек скрипт после неудачной команды продолжит работу как ни в чем не бывало. Например, попытается заархивировать каталог, который не удалось создать.

Для проверки синтаксиса без запуска используйте:

bash -n script.sh

А для полноценного анализа кода – утилиту ShellCheck:

apt install shellcheck   # Debian/Ubuntu
shellcheck script.sh

Практические примеры скриптов

Резервное копирование сайта и базы данных

#!/bin/bash
# Количество дней для хранения копий
days=14
# Путь к папке с файлами для хранения файлов копий
backup_dir="backups"
# Имя директории сайта
site_dir="yoursite.com"
# Имя базы данных
db_name="login_db"
# Пароль для доступа к базе данных
db_pass="Mpo2Dkm"
d=`date +%F-%H:%M:%S`
mkdir -p $backup_dir 2>/dev/null
# Удаление старых файлов копий
find $backup_dir -name "*autobackup.tar.gz" -type f -mtime +$days -delete
# Дамп базы данных
dump_name="$site_dir/dump-$d.sql"
mysqldump -h localhost -u$db_name $db_name -p"$db_pass" > $dump_name
# Архивирование файлов
tar cvvzf "$backup_dir/$site_dir-$d-autobackup.tar.gz" $site_dir
rm $dump_name

Проверка доступности сайта

#!/bin/bash
URL="https://example.com"
code=$(curl -o /dev/null -s -w "%{http_code}" --max-time 10 "$URL")
if [ "$code" -ne 200 ]; then
    echo "[$(date)] $URL недоступен, код ответа: $code" >> /var/log/site-check.log
else
    echo "[$(date)] $URL работает нормально"
fi

FAQ

Почему при запуске появляется ошибка «Permission denied»?

У файла нет бита выполнения. Выполните chmod +x script.sh. Если ошибка сохраняется, проверьте, что раздел не смонтирован с опцией noexec.

В чем разница между ./script.sh и source script.sh?

Первый вариант запускает скрипт в отдельном процессе – изменения переменных не сохранятся в вашей сессии. Второй выполняет команды в текущей оболочке, поэтому переменные и функции останутся доступны после завершения.

Можно ли запускать Bash-скрипты на виртуальном хостинге?

Да, это возможно, но установка пакетов и настройка системных служб доступны на VPS и выделенных серверах, где вы работаете с правами root.

Чем Bash отличается от Python для автоматизации?

Bash идеален для последовательного вызова системных утилит и работы с файлами: то, что в Python занимает десяток строк с импортами, здесь помещается в одну. Но как только появляются сложные структуры данных, работа с API или объемная логика, читаемость Bash резко падает – тогда стоит переходить на Python.

Заключение

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

Bash помогает превратить рутинные операции с Linux-сервером в автоматизированные сценарии – быстрее, надежнее и с меньшим количеством ручной работы.
Илья Пилипенко, технический писатель в Beget

Если возникнут вопросы, напишите нам, пожалуйста, тикет из панели управления аккаунта (раздел “Помощь и поддержка”), а если вы захотите обсудить наши продукты с коллегами по цеху и сотрудниками Beget – ждем вас в нашем сообществе в Telegram.