Skip to content

Latest commit

 

History

History
754 lines (554 loc) · 26.4 KB

File metadata and controls

754 lines (554 loc) · 26.4 KB

Этап 6. Основы bash-скриптов

Зачем нужны скрипты

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

Представьте: каждый вечер вы должны проверить лог-файл nginx, подсчитать количество ошибок и сохранить отчёт. Это три-четыре команды, которые нужно вводить каждый день. Вместо этого можно записать их в файл и запускать одной командой. Такой файл называется скриптом (script).

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

Привязка к проекту. К концу этой главы мы напишем скрипт, который создаёт резервную копию нашего проекта WordPress — проверяет, что нужный каталог существует, архивирует его и сообщает об успехе или ошибке.

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

Врезка: переменные окружения и PATH

Что такое переменные окружения

Когда вы входите в систему, оболочка bash создаёт набор переменных окружения (environment variables) — именованных значений, которые доступны всем запускаемым программам. Они хранят настройки системы и пользователя.

Посмотреть все переменные окружения можно командой env:

env

Вывод будет длинным. Вот несколько важных переменных:

Переменная Описание Пример значения
HOME Домашний каталог пользователя /home/student
USER Имя текущего пользователя student
SHELL Путь к оболочке пользователя /bin/bash
PWD Текущий рабочий каталог /home/student
LANG Язык и локаль ru_RU.UTF-8
PATH Где искать программы /usr/local/bin:/usr/bin:/bin
EDITOR Текстовый редактор по умолчанию nano

Чтобы посмотреть значение конкретной переменной, используйте echo и знак $ перед именем:

echo $HOME
/home/student
echo $USER
student

Знак $ говорит bash: «подставь значение переменной». Без $ bash воспримет слово HOME как обычный текст:

echo HOME
HOME

Создание и экспорт переменных

Вы можете создать свою переменную:

GREETING="Привет, мир"
echo $GREETING
Привет, мир

Обратите внимание: вокруг знака = не должно быть пробелов. Запись GREETING = "Привет" вызовет ошибку — bash решит, что GREETING — это команда, а = и "Привет" — её аргументы.

Такая переменная существует только в текущей оболочке. Если вы запустите из неё другую программу (дочерний процесс), она эту переменную не увидит. Чтобы переменная стала доступна дочерним процессам, её нужно экспортировать:

export GREETING="Привет, мир"

Именно поэтому в главе 04, когда мы задавали редактор по умолчанию, мы писали export EDITOR=nano — чтобы все программы (git, crontab и т.д.) видели эту настройку.

PATH — как система находит команды

Когда вы вводите команду, например ls, bash должен найти файл программы ls на диске. Но как он знает, где искать?

Переменная PATH содержит список каталогов, разделённых двоеточием. Bash просматривает их по порядку, пока не найдёт файл с нужным именем:

echo $PATH
/usr/local/bin:/usr/bin:/bin:/usr/local/sbin:/usr/sbin:/sbin

Это значит: сначала искать в /usr/local/bin, потом в /usr/bin, потом в /bin и так далее.

Команда which показывает, где именно bash нашёл программу:

which ls
/usr/bin/ls
which nano
/usr/bin/nano

Если программа не находится ни в одном каталоге из PATH, bash выдаст ошибку «command not found». Именно поэтому для запуска скрипта из текущего каталога нужно писать ./script.sh — текущий каталог . обычно не входит в PATH.

Файл ~/.bashrc

Переменные окружения, заданные в терминале, исчезают при выходе из системы. Чтобы настройки сохранялись, их записывают в файл ~/.bashrc — скрипт, который bash автоматически выполняет при каждом входе в систему.

В главе 04 мы уже добавляли туда строку export EDITOR=nano. Посмотрим, что ещё есть в этом файле:

cat ~/.bashrc

Вы увидите настройки приглашения командной строки (PS1), алиасы и другие параметры. Если вы добавили что-то в ~/.bashrc, но изменения не применились, выполните:

source ~/.bashrc

Команда source выполняет файл в текущей оболочке, применяя все настройки без необходимости выходить и входить заново.


Первый скрипт

Создание

Создадим простой скрипт в nano:

nano ~/wordpress-project/hello.sh

Введите:

#!/bin/bash

echo "Привет! Сейчас:"
date
echo "Вы находитесь в каталоге:"
pwd

Сохраните и выйдите (Ctrl+X, Y, Enter).

Shebang

Первая строка #!/bin/bash называется shebang (или «шебанг»). Она сообщает системе, какой интерпретатор использовать для выполнения скрипта.

#!/bin/bash
││
│└─ путь к интерпретатору
└── специальная последовательность символов (#!)

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

Запуск

Попробуем запустить:

./wordpress-project/hello.sh
bash: ./wordpress-project/hello.sh: Отказано в доступе

Мы уже сталкивались с этим в главе 03 — у файла нет права на выполнение. Добавим:

chmod u+x ~/wordpress-project/hello.sh

Запустим ещё раз:

./wordpress-project/hello.sh
Привет! Сейчас:
Сб мар 15 12:00:00 MSK 2026
Вы находитесь в каталоге:
/home/student

Почему перед именем файла ./? Как мы выяснили в разделе про PATH, текущий каталог не входит в список каталогов для поиска программ. Префикс ./ явно указывает: «запусти файл из текущего каталога». Без него bash будет искать программу hello.sh в каталогах PATH и не найдёт.

Альтернативный способ запуска

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

bash ~/wordpress-project/hello.sh

В этом случае chmod +x не нужен — bash сам читает файл и выполняет команды. Но в повседневной работе удобнее делать скрипт исполняемым и запускать через ./.

Переменные в скриптах

Объявление и использование

Переменные в скриптах работают так же, как в командной строке:

#!/bin/bash

PROJECT_DIR="$HOME/wordpress-project"
BACKUP_DIR="$HOME/backups"

echo "Проект: $PROJECT_DIR"
echo "Резервные копии: $BACKUP_DIR"

Имена переменных принято писать ЗАГЛАВНЫМИ_БУКВАМИ — это не обязательно, но помогает отличать переменные от команд.

Фигурные скобки

Иногда bash не может понять, где заканчивается имя переменной. Для этого используются фигурные скобки ${...}:

FILE="access"
echo "$FILE_log"      # Ошибка! bash ищет переменную FILE_log
echo "${FILE}_log"    # Правильно: access_log

Кавычки

В bash есть два вида кавычек, и они работают по-разному:

Кавычки Поведение Пример Результат
Двойные "..." Раскрывают переменные "Привет, $USER" Привет, student
Одинарные '...' Всё буквально 'Привет, $USER' Привет, $USER
#!/bin/bash

NAME="мир"
echo "Привет, $NAME"    # Привет, мир
echo 'Привет, $NAME'    # Привет, $NAME

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

Подстановка команд

Результат выполнения команды можно сохранить в переменную с помощью $(...):

#!/bin/bash

TODAY=$(date +%Y-%m-%d)
FILE_COUNT=$(ls ~/wordpress-project | wc -l)

echo "Сегодня: $TODAY"
echo "Файлов в проекте: $FILE_COUNT"
Сегодня: 2026-03-15
Файлов в проекте: 5

Конструкция $(команда) выполняет команду и подставляет её вывод вместо себя.

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

Скрипту можно передавать аргументы при запуске — как опции обычным командам:

./script.sh аргумент1 аргумент2 аргумент3

Внутри скрипта аргументы доступны через специальные переменные:

Переменная Значение
$0 Имя самого скрипта
$1 Первый аргумент
$2 Второй аргумент
$# Количество аргументов
$@ Все аргументы

Пример — скрипт, который приветствует пользователя по имени:

#!/bin/bash

echo "Скрипт: $0"
echo "Привет, $1!"
echo "Передано аргументов: $#"
chmod u+x greet.sh
./greet.sh Евгений
Скрипт: ./greet.sh
Привет, Евгений!
Передано аргументов: 1

Условия (if)

Базовый синтаксис

if [ условие ]; then
    # команды, если условие истинно
fi

Важно: пробелы внутри квадратных скобок обязательны. [ -f file ] — правильно, [-f file] — ошибка.

Примеры условий

Условие Что проверяет
[ -f файл ] Файл существует и является обычным файлом
[ -d каталог ] Каталог существует
[ -e путь ] Путь существует (файл или каталог)
[ -z "$строка" ] Строка пуста
[ -n "$строка" ] Строка не пуста
[ "$a" = "$b" ] Строки равны
[ "$a" != "$b" ] Строки не равны
[ "$a" -eq "$b" ] Числа равны (equal)
[ "$a" -gt "$b" ] a больше b (greater than)
[ "$a" -lt "$b" ] a меньше b (less than)

if / else / elif

#!/bin/bash

FILE="$HOME/wordpress-project/config/nginx.conf"

if [ -f "$FILE" ]; then
    echo "Файл $FILE найден"
else
    echo "Файл $FILE не существует!"
fi

Для нескольких условий используется elif (else if):

#!/bin/bash

if [ $# -eq 0 ]; then
    echo "Ошибка: укажите имя файла"
elif [ -f "$1" ]; then
    echo "Файл $1 существует, размер:"
    wc -c "$1"
else
    echo "Файл $1 не найден"
fi

Проверка результата команды

if может проверять результат выполнения команды. Если команда завершилась успешно (код возврата 0), условие считается истинным:

#!/bin/bash

if grep -q '404' ~/wordpress-project/logs/access.log; then
    echo "Внимание: в логе есть ошибки 404!"
else
    echo "Ошибок 404 не найдено"
fi

Флаг -q (quiet) заставляет grep работать молча — не выводить совпадения, а только вернуть код успеха или неуспеха.

Циклы

for — перебор списка

Цикл for выполняет команды для каждого элемента списка:

for переменная in список; do
    # команды
done

Примеры:

#!/bin/bash

# Перебор явного списка
for CITY in Москва Петербург Новосибирск; do
    echo "Город: $CITY"
done
Город: Москва
Город: Петербург
Город: Новосибирск
#!/bin/bash

# Перебор файлов в каталоге
for FILE in ~/wordpress-project/config/*; do
    echo "Конфигурационный файл: $FILE"
done
#!/bin/bash

# Перебор аргументов скрипта
for ARG in "$@"; do
    echo "Аргумент: $ARG"
done

while — цикл с условием

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

while [ условие ]; do
    # команды
done

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

#!/bin/bash

while read LINE; do
    echo "Строка: $LINE"
done < ~/wordpress-project/logs/access.log

Конструкция < файл в конце цикла — это перенаправление ввода (из главы 05): содержимое файла подаётся на stdin команды read, которая считывает по одной строке.

Пример — обратный отсчёт:

#!/bin/bash

COUNT=5
while [ $COUNT -gt 0 ]; do
    echo "$COUNT..."
    COUNT=$((COUNT - 1))
done
echo "Поехали!"
5...
4...
3...
2...
1...
Поехали!

Запись $((выражение)) — это арифметическое вычисление в bash.

Коды возврата

Что это такое

Каждая команда в Linux при завершении возвращает числовой код возврата (exit code, exit status):

  • 0 — команда завершилась успешно;
  • не 0 (1, 2, 127 и т.д.) — произошла ошибка.

Код возврата последней команды хранится в специальной переменной $?:

ls /etc/hostname
echo $?
/etc/hostname
0
ls /несуществующий-файл
echo $?
ls: невозможно получить доступ к '/несуществующий-файл': Нет такого файла или каталога
2

exit в скриптах

Команда exit завершает скрипт с указанным кодом возврата:

#!/bin/bash

if [ $# -eq 0 ]; then
    echo "Ошибка: не указано имя файла" >&2
    exit 1
fi

echo "Обработка файла $1..."
exit 0

Запись >&2 перенаправляет сообщение об ошибке в stderr (поток ошибок), как мы узнали в главе 05. Это хорошая практика — сообщения об ошибках отправлять в stderr, а обычный вывод — в stdout.

Соглашение: exit 0 — успех, exit 1 — общая ошибка. Можно использовать и другие числа для разных типов ошибок, но для простых скриптов достаточно 0 и 1.

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

Соберём все знания воедино и напишем полезный скрипт для нашего проекта. Он будет создавать резервную копию каталога wordpress-project.

nano ~/wordpress-project/backup.sh

Введите:

#!/bin/bash

# Скрипт резервного копирования проекта WordPress
# Использование: ./backup.sh [каталог_для_бэкапов]

# Настройки
PROJECT_DIR="$HOME/wordpress-project"
BACKUP_DIR="${1:-$HOME/backups}"
DATE=$(date +%Y-%m-%d_%H-%M-%S)
ARCHIVE_NAME="wordpress-backup-${DATE}.tar.gz"

# Проверяем, что каталог проекта существует
if [ ! -d "$PROJECT_DIR" ]; then
    echo "Ошибка: каталог проекта $PROJECT_DIR не найден" >&2
    exit 1
fi

# Создаём каталог для бэкапов, если его нет
if [ ! -d "$BACKUP_DIR" ]; then
    echo "Создаю каталог для бэкапов: $BACKUP_DIR"
    mkdir -p "$BACKUP_DIR"
fi

# Создаём архив
echo "Создаю резервную копию..."
tar czf "$BACKUP_DIR/$ARCHIVE_NAME" -C "$HOME" wordpress-project

# Проверяем результат
if [ $? -eq 0 ]; then
    echo "Готово! Резервная копия сохранена:"
    echo "  $BACKUP_DIR/$ARCHIVE_NAME"
    echo "  Размер: $(du -h "$BACKUP_DIR/$ARCHIVE_NAME" | cut -f1)"
else
    echo "Ошибка при создании резервной копии!" >&2
    exit 1
fi

Сохраните файл и сделайте его исполняемым:

chmod u+x ~/wordpress-project/backup.sh

Запустите:

~/wordpress-project/backup.sh
Создаю каталог для бэкапов: /home/student/backups
Создаю резервную копию...
Готово! Резервная копия сохранена:
  /home/student/backups/wordpress-backup-2026-03-15_12-00-00.tar.gz
  Размер: 4.0K

Разберём несколько моментов в этом скрипте:

  • ${1:-$HOME/backups} — если первый аргумент не передан, используется значение по умолчанию $HOME/backups. Конструкция ${переменная:-значение} означает «подставь значение переменной, а если она пуста — подставь значение после :-».
  • tar czf — создаёт сжатый архив (tar + gzip). Подробнее о tar вы узнаете в дальнейшей работе, пока достаточно знать, что эта команда упаковывает каталог в один файл.
  • [ ! -d ... ] — восклицательный знак означает отрицание: «если каталог НЕ существует».
  • du -h — показывает размер файла в читаемом формате (K, M, G).

Можно указать другой каталог для бэкапов:

~/wordpress-project/backup.sh /tmp/my-backups

Практическое задание

  1. Напишите скрипт sysinfo.sh, который выводит:

    • имя пользователя ($USER);
    • имя хоста (hostname);
    • текущую дату и время (date);
    • текущий каталог (pwd). Сделайте его исполняемым и запустите.
  2. Напишите скрипт check-file.sh, который принимает имя файла как аргумент и сообщает, существует он или нет. Если аргумент не передан — выведите подсказку по использованию и завершите скрипт с кодом 1.

  3. Напишите скрипт count-errors.sh, который подсчитывает количество строк с кодом 403 и с кодом 404 в файле ~/wordpress-project/logs/access.log и выводит результат:

    Ошибок 403: 4
    Ошибок 404: 1
    

    Подсказка: используйте grep -c и подстановку команд $(...).

  4. Напишите скрипт list-configs.sh, который с помощью цикла for перебирает все файлы в ~/wordpress-project/config/ и для каждого выводит его имя и размер.

  5. Напишите скрипт greet.sh, который принимает имя как аргумент. Если имя не передано, скрипт должен спросить: «Как вас зовут?» и прочитать ответ командой read. Подсказка:

    read -p "Как вас зовут? " NAME
  6. Измените скрипт backup.sh так, чтобы он сохранял не более трёх последних бэкапов, удаляя самые старые. Подсказка: используйте ls -t (сортировка по времени) и tail -n +4 (строки начиная с четвёртой).

Итоги

В этой главе вы:

  • узнали, зачем нужны скрипты и как они упрощают работу;
  • разобрались с переменными окружения и их ролью в системе;
  • поняли, как PATH определяет, где bash ищет команды;
  • познакомились с файлом ~/.bashrc и командой source;
  • написали первый скрипт: shebang, chmod +x, запуск через ./;
  • научились работать с переменными, кавычками и подстановкой команд;
  • освоили аргументы скрипта ($1, $2, $#, $@);
  • научились использовать условия (if, else, elif) и циклы (for, while);
  • разобрались с кодами возврата и командой exit;
  • написали реальный скрипт резервного копирования для проекта.

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

Во второй части — «Системный администратор» — мы перейдём к управлению системой: установке программ, управлению пользователями, работе с сетью и в итоге развернём наш сайт на nginx + PHP-FPM + WordPress.

Следующая глава — управление пакетами (apt): как устанавливать, обновлять и удалять программы в Debian.