В предыдущих главах вы научились работать с файлами, правами, текстовыми редакторами и утилитами обработки текста. Каждый раз вы вводили команды вручную — одну за другой. Но что, если одну и ту же последовательность команд нужно выполнять регулярно?
Представьте: каждый вечер вы должны проверить лог-файл nginx, подсчитать количество ошибок и сохранить отчёт. Это три-четыре команды, которые нужно вводить каждый день. Вместо этого можно записать их в файл и запускать одной командой. Такой файл называется скриптом (script).
Скрипт — это текстовый файл с последовательностью команд, которые выполняются автоматически, одна за другой.
Привязка к проекту. К концу этой главы мы напишем скрипт, который создаёт резервную копию нашего проекта WordPress — проверяет, что нужный каталог существует, архивирует его и сообщает об успехе или ошибке.
Прежде чем писать скрипты, нам нужно разобраться с важной темой — как система находит программы и что такое переменные окружения.
Когда вы входите в систему, оболочка 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 $USERstudent
Знак $ говорит bash: «подставь значение переменной». Без $ bash воспримет
слово HOME как обычный текст:
echo HOMEHOME
Вы можете создать свою переменную:
GREETING="Привет, мир"
echo $GREETINGПривет, мир
Обратите внимание: вокруг знака
=не должно быть пробелов. ЗаписьGREETING = "Привет"вызовет ошибку — bash решит, чтоGREETING— это команда, а=и"Привет"— её аргументы.
Такая переменная существует только в текущей оболочке. Если вы запустите из неё другую программу (дочерний процесс), она эту переменную не увидит. Чтобы переменная стала доступна дочерним процессам, её нужно экспортировать:
export GREETING="Привет, мир"Именно поэтому в главе 04, когда мы задавали редактор по умолчанию, мы писали
export EDITOR=nano — чтобы все программы (git, crontab и т.д.) видели
эту настройку.
Когда вы вводите команду, например 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 — скрипт,
который 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).
Первая строка #!/bin/bash называется shebang (или «шебанг»). Она
сообщает системе, какой интерпретатор использовать для выполнения скрипта.
#!/bin/bash
││
│└─ путь к интерпретатору
└── специальная последовательность символов (#!)
Без shebang система не будет знать, что этот файл нужно выполнять через bash. Всегда начинайте скрипты с этой строки.
Попробуем запустить:
./wordpress-project/hello.shbash: ./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 [ условие ]; 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) |
#!/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 не найден"
fiif может проверять результат выполнения команды. Если команда завершилась
успешно (код возврата 0), условие считается истинным:
#!/bin/bash
if grep -q '404' ~/wordpress-project/logs/access.log; then
echo "Внимание: в логе есть ошибки 404!"
else
echo "Ошибок 404 не найдено"
fiФлаг -q (quiet) заставляет grep работать молча — не выводить совпадения,
а только вернуть код успеха или неуспеха.
Цикл 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 [ условие ]; 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 завершает скрипт с указанным кодом возврата:
#!/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-
Напишите скрипт
sysinfo.sh, который выводит:- имя пользователя (
$USER); - имя хоста (
hostname); - текущую дату и время (
date); - текущий каталог (
pwd). Сделайте его исполняемым и запустите.
- имя пользователя (
-
Напишите скрипт
check-file.sh, который принимает имя файла как аргумент и сообщает, существует он или нет. Если аргумент не передан — выведите подсказку по использованию и завершите скрипт с кодом 1. -
Напишите скрипт
count-errors.sh, который подсчитывает количество строк с кодом 403 и с кодом 404 в файле~/wordpress-project/logs/access.logи выводит результат:Ошибок 403: 4 Ошибок 404: 1Подсказка: используйте
grep -cи подстановку команд$(...). -
Напишите скрипт
list-configs.sh, который с помощью циклаforперебирает все файлы в~/wordpress-project/config/и для каждого выводит его имя и размер. -
Напишите скрипт
greet.sh, который принимает имя как аргумент. Если имя не передано, скрипт должен спросить: «Как вас зовут?» и прочитать ответ командойread. Подсказка:read -p "Как вас зовут? " NAME
-
Измените скрипт
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.