Базовые конструкции Shell: переменные, условия и циклы
Базовые конструкции Shell: переменные, условия и циклы
Представьте ситуацию: вам нужно переименовать тысячу лог-файлов, добавив к каждому текущую дату, а затем скопировать их на резервный сервер. Вручную эта задача займет несколько часов монотонного труда. Скрипт на Bash выполнит ее за доли секунды. Умение писать сценарии командной строки превращает обычного пользователя операционной системы в инженера, способного автоматизировать любые рутинные процессы.
Написание скриптов начинается с понимания того, как программа хранит данные, принимает решения и повторяет действия.
Переменные: память вашего скрипта
Как скрипт запоминает информацию? Для этого используются переменные — именованные области памяти, в которые можно поместить текст, числа или результаты выполнения других команд.
В Shell создание переменной происходит без указания ее типа. Главное правило синтаксиса — строгое отсутствие пробелов вокруг знака равенства.
USER_NAME="Anna"
FILES_COUNT=150
Чтобы получить значение переменной, перед ее именем ставится знак доллара: echo $USER_NAME.
Часто возникает необходимость задать значение по умолчанию. Например, если пользователь не передал путь к директории, скрипт должен использовать текущую папку. Для этого применяется специальный синтаксис подстановки:
TARGET_DIR=${1:-"/tmp/backup"}
echo "Резервное копирование в директорию: $TARGET_DIR"
Если первый аргумент скрипта ($1) пуст или не задан, переменная TARGET_DIR автоматически получит значение /tmp/backup.
Сохранение результатов системных вызовов
Скрипты часто выступают связующим звеном между различными системными утилитами Unix. Чтобы сохранить результат работы системного вызова или внешней программы в переменную, используется синтаксис $(команда).
Например, нам нужно создать архив, в названии которого будет зафиксирована текущая дата. Для этого мы вызываем системную утилиту date:
CURRENT_DATE=$(date +%Y-%m-%d)
ARCHIVE_NAME="backup_$CURRENT_DATE.tar.gz"
echo "Будет создан архив: $ARCHIVE_NAME"
Если скрипт запущен 25 октября 2023 года, переменная ARCHIVE_NAME получит значение backup_2023-10-25.tar.gz. Это базовый паттерн при работе с файловой системой, позволяющий избежать случайной перезаписи старых резервных копий.
Одинарные и двойные кавычки
При работе со строками критически важно различать типы кавычек. Они определяют, будет ли интерпретатор обрабатывать специальные символы внутри текста.
| Тип кавычек | Название | Поведение интерпретатора | Пример | Результат |
|---|---|---|---|---|
" " |
Двойные | Раскрывает значения переменных и выполняет команды | echo "Привет, $USER_NAME" |
Привет, Anna |
' ' |
Одинарные | Экранирует все символы, воспринимает текст буквально | echo 'Привет, $USER_NAME' |
Привет, $USER_NAME |
` ` |
Обратные | Выполняет команду внутри и возвращает ее вывод | echo "Файлов: \ls | wc -l`"` |
Файлов: 42 |
Программирование — это искусство разбивать сложную задачу на последовательность простых шагов, которые может выполнить машина.
Условные конструкции: логика принятия решений
Скрипт редко выполняется линейно. Обычно ему нужно реагировать на внешние обстоятельства: проверять наличие файлов, права доступа или объем свободного места. За эту логику отвечают условные выражения.
Базовый синтаксис условия выглядит так:
if [ условие ]; then
# команды, если условие истинно
elif [ другое_условие ]; then
# команды, если второе условие истинно
else
# команды, если все условия ложны
fi
Математические и строковые сравнения
В классической математике для сравнения чисел используются стандартные операторы: , , , , . В Shell внутри квадратных скобок [ ] применяются специальные буквенные флаги, так как символы больше и меньше зарезервированы для перенаправления потоков ввода-вывода.
- записывается как
-eq(equal) - записывается как
-ne(not equal) - записывается как
-gt(greater than) - записывается как
-ge(greater or equal) - записывается как
-lt(less than) - записывается как
-le(less or equal)
Пример скрипта, который проверяет количество переданных аргументов. Если их меньше двух, программа завершается с ошибкой:
ARGS_COUNT=$#
if [ "$ARGS_COUNT" -lt 2 ]; then
echo "Ошибка: требуется минимум 2 аргумента."
exit 1
fi
Работа с файловой системой Unix
Одной из главных задач системного администратора является взаимодействие с файлами. Bash предоставляет встроенные операторы для проверки состояния файловой системы:
-f FILE— существует ли обычный файл.-d DIR— существует ли директория.-r FILE— доступен ли файл для чтения.-x FILE— является ли файл исполняемым.
Допустим, нам нужно создать резервную копию базы данных, но только если директория для бэкапов существует. Если ее нет — скрипт должен ее создать.
BACKUP_DIR="/var/backups/db"
if [ ! -d "$BACKUP_DIR" ]; then
echo "Директория не найдена. Создаем: $BACKUP_DIR"
mkdir -p "$BACKUP_DIR"
else
echo "Директория $BACKUP_DIR уже существует. Начинаем копирование."
fi
Восклицательный знак ! перед условием инвертирует его, превращая логическое «если существует» в «если не существует».
Комбинирование условий: логические И и ИЛИ
Часто требуется проверить сразу несколько параметров. Для этого применяются логические операторы:
&&(Логическое И) — истинно, если оба условия верны.||(Логическое ИЛИ) — истинно, если хотя бы одно условие верно.
Пример: скрипт должен удалить временный файл, но только если это действительно файл (не директория) и у нас есть права на его удаление (права на запись).
TEMP_FILE="/tmp/cache.bin"
if [ -f "$TEMP_FILE" ] && [ -w "$TEMP_FILE" ]; then
rm "$TEMP_FILE"
echo "Временный файл успешно удален."
fi
Циклы: автоматизация повторяющихся действий
Когда нужно выполнить одну и ту же операцию для множества объектов, на помощь приходят циклы. В Shell наиболее популярны два вида циклов: for и while.
Цикл for: перебор элементов
Цикл for идеально подходит для итерации по спискам, массивам или файлам в директории. Он берет каждый элемент из заданного набора и выполняет для него блок кода.
Синтаксис:
for ПЕРЕМЕННАЯ in СПИСОК; do
# команды
done
Рассмотрим реальную задачу: у нас есть папка с изображениями в формате JPG, и нам нужно изменить их расширение на PNG.
for image in *.jpg; do
new_name="${image%.jpg}.png"
mv "$image" "$new_name"
echo "Файл $image переименован в $new_name"
done
В этом примере *.jpg раскрывается в список всех файлов с таким расширением в текущей директории. Конструкция ${image%.jpg} — это встроенная функция Bash для удаления подстроки с конца переменной.
Также for умеет работать с числовыми диапазонами. Если нужно создать 5 тестовых пользователей:
for i in {1..5}; do
echo "Создание пользователя test_user_$i"
# Здесь мог бы быть системный вызов useradd
done
Цикл while: выполнение по условию
В отличие от for, цикл while выполняется до тех пор, пока истинно заданное условие. Он незаменим, когда количество итераций заранее неизвестно.
Частый сценарий использования while — построчное чтение текстового файла. Например, у нас есть файл servers.txt со списком IP-адресов, и мы хотим проверить доступность каждого из них:
while read -r ip_address; do
if ping -c 1 -W 1 "$ip_address" > /dev/null; then
echo "Сервер $ip_address доступен"
else
echo "Сервер $ip_address НЕ отвечает"
fi
done < servers.txt
Здесь команда read считывает строку из файла и помещает ее в переменную ip_address. Цикл продолжается, пока в файле не закончатся строки. Перенаправление < servers.txt в конце цикла передает содержимое файла на стандартный ввод цикла.
Управление потоком: break и continue
Иногда требуется прервать выполнение цикла досрочно или пропустить текущую итерацию. Для этого существуют операторы break и continue.
breakполностью останавливает цикл и передает управление следующей за ним команде.continueпрерывает только текущую итерацию и переходит к следующему элементу.
Пример использования break: поиск конкретного файла в архиве. Как только файл найден, продолжать перебор остальных файлов бессмысленно.
for file in /var/log/*.log; do
if grep -q "CRITICAL ERROR" "$file"; then
echo "Критическая ошибка найдена в файле: $file"
break
fi
done
В данном случае, если в директории 100 лог-файлов, и ошибка нашлась во втором, скрипт не будет проверять оставшиеся 98 файлов, сэкономив вычислительные ресурсы и время выполнения.
Итоги
- Переменные в Shell создаются без указания типов и строго без пробелов вокруг знака равенства. Для безопасной подстановки значений по умолчанию используется синтаксис
${VAR:-default}. - Одинарные кавычки экранируют все символы, воспринимая текст буквально, а двойные позволяют интерпретатору раскрывать значения переменных и выполнять встроенные команды.
- Условные конструкции
if-then-elseопираются на буквенные операторы (например,-gtдля ) при сравнении чисел и на специальные флаги (-f,-d) при проверке объектов файловой системы Unix. - Циклы
forэффективны для перебора известных списков и файлов, аwhileотлично справляется с чтением данных построчно и выполнением действий до изменения определенного условия.