8. Bash скрипты
Зачем нужны bash скрипты?
Заголовок раздела «Зачем нужны bash скрипты?»Bash скрипты — это автоматизация. Вместо того чтобы каждый раз вводить 10 команд вручную, ты пишешь скрипт и запускаешь его одной командой. Деплой, бэкапы, обработка данных — всё это делается через bash скрипты.
Создание и запуск скрипта
Заголовок раздела «Создание и запуск скрипта»#!/bin/bash# Первая строка — shebang: путь к интерпретатору# Это обязательно для исполняемых скриптов
echo "Привет, мир!"# Создать и запуститьnano myscript.sh # или vim, или любой редакторchmod +x myscript.sh # сделать исполняемым./myscript.sh # запустить
# Или через bash напрямую (без chmod)bash myscript.shПеременные
Заголовок раздела «Переменные»#!/bin/bash
# Объявление переменных (без пробелов вокруг =!)NAME="John"AGE=30GREETING="Привет"
# Использование переменныхecho "Меня зовут $NAME"echo "Мне ${AGE} лет" # фигурные скобки для однозначностиecho "${GREETING}, ${NAME}!"
# Переменная из результата командыCURRENT_DATE=$(date +%Y-%m-%d)FILES_COUNT=$(ls -1 | wc -l)echo "Дата: $CURRENT_DATE, файлов: $FILES_COUNT"
# АрифметикаA=10B=3SUM=$((A + B))PRODUCT=$((A * B))REMAINDER=$((A % B))echo "Сумма: $SUM, произведение: $PRODUCT, остаток: $REMAINDER"
# Только для чтенияreadonly CONFIG_DIR="/etc/myapp"
# Удалить переменнуюunset NAMEАргументы скрипта
Заголовок раздела «Аргументы скрипта»#!/bin/bash# Запуск: ./script.sh arg1 arg2 arg3
echo "Имя скрипта: $0"echo "Первый аргумент: $1"echo "Второй аргумент: $2"echo "Все аргументы: $@"echo "Количество аргументов: $#"
# Проверка аргументовif [ $# -lt 2 ]; then echo "Использование: $0 <имя> <возраст>" exit 1fi
echo "Привет, $1! Тебе $2 лет."Условия: if/else
Заголовок раздела «Условия: if/else»#!/bin/bash
AGE=25
# Числовые сравнения: -eq, -ne, -lt, -le, -gt, -geif [ $AGE -ge 18 ]; then echo "Совершеннолетний"elif [ $AGE -ge 14 ]; then echo "Подросток"else echo "Ребёнок"fi
# Строковые сравненияNAME="admin"if [ "$NAME" = "admin" ]; then echo "Доступ разрешён"fi
if [ "$NAME" != "guest" ]; then echo "Не гость"fi
# Проверка файлов и директорийFILE="/etc/passwd"if [ -f "$FILE" ]; then echo "$FILE существует и является файлом"fi
if [ -d "/home" ]; then echo "/home — это директория"fi
if [ -r "$FILE" ]; then echo "Файл доступен для чтения"fi
# Сложные условияif [ $AGE -ge 18 ] && [ "$NAME" = "admin" ]; then echo "Взрослый администратор"fi
# [[ ]] — расширенный синтаксис (bash, не POSIX)if [[ $NAME == adm* ]]; then # glob-паттерны echo "Имя начинается с adm"fi
if [[ $NAME =~ ^[a-z]+$ ]]; then # регулярные выражения echo "Имя только из строчных букв"fiЦиклы: for
Заголовок раздела «Циклы: for»#!/bin/bash
# Цикл по спискуfor NAME in Alice Bob Charlie; do echo "Привет, $NAME!"done
# Цикл по диапазону чиселfor i in {1..5}; do echo "Итерация $i"done
# Цикл with stepfor i in {0..20..5}; do echo $i # 0 5 10 15 20done
# C-style циклfor ((i=0; i<10; i++)); do echo "i = $i"done
# Цикл по файламfor FILE in *.txt; do echo "Обрабатываю: $FILE" # wc -l "$FILE"done
# Цикл по строкам файлаwhile IFS= read -r LINE; do echo "Строка: $LINE"done < input.txtЦиклы: while
Заголовок раздела «Циклы: while»#!/bin/bash
# Цикл whileCOUNT=1while [ $COUNT -le 5 ]; do echo "Итерация $COUNT" COUNT=$((COUNT + 1))done
# Чтение ввода пользователяecho "Введите 'quit' для выхода:"while true; do read -p "> " INPUT if [ "$INPUT" = "quit" ]; then break fi echo "Ты ввёл: $INPUT"done
# until — пока условие НЕ выполненоCOUNT=1until [ $COUNT -gt 5 ]; do echo "Count: $COUNT" ((COUNT++))doneФункции
Заголовок раздела «Функции»#!/bin/bash
# Объявление функцииgreet() { local NAME="$1" # local — переменная локальная для функции local AGE="$2" echo "Привет, $NAME! Тебе $AGE лет."}
# Вызов функцииgreet "Alice" 25greet "Bob" 30
# Функция с возвращаемым значениемadd() { local A=$1 local B=$2 echo $((A + B)) # возвращаем через echo}
RESULT=$(add 10 20)echo "10 + 20 = $RESULT"
# Функция с кодом возвратаcheck_file() { if [ -f "$1" ]; then return 0 # успех else return 1 # ошибка fi}
if check_file "/etc/passwd"; then echo "Файл существует"else echo "Файл не найден"fiКоды возврата и обработка ошибок
Заголовок раздела «Коды возврата и обработка ошибок»#!/bin/bash
# $? — код возврата последней командыls /nonexistent 2>/dev/nullecho "Код возврата: $?" # 2 (ошибка)
ls /etcecho "Код возврата: $?" # 0 (успех)
# set -e — прерывать скрипт при ошибкеset -e
# Проверка каждой командыmkdir /tmp/mydir || { echo "Не удалось создать директорию"; exit 1; }
# trap — перехват сигналов и завершенияcleanup() { echo "Очищаю временные файлы..." rm -rf /tmp/myapp_*}trap cleanup EXIT # вызвать при любом выходеtrap cleanup INT TERM # вызвать при Ctrl+C
# Практический шаблон скриптаset -euo pipefail # e=exit on error, u=undefined vars, o=pipe errors
log() { echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1"}
log "Скрипт запущен"log "Выполнено успешно"Практика: Реальный скрипт бэкапа
Заголовок раздела «Практика: Реальный скрипт бэкапа»#!/bin/bashset -euo pipefail
# КонфигурацияSOURCE_DIR="${1:-$HOME}"BACKUP_DIR="/tmp/backups"DATE=$(date +%Y%m%d_%H%M%S)ARCHIVE="${BACKUP_DIR}/backup_${DATE}.tar.gz"
# Функцииlog() { echo "[$(date '+%H:%M:%S')] $1"; }error() { echo "ОШИБКА: $1" >&2; exit 1; }
# Проверки[ -d "$SOURCE_DIR" ] || error "Директория $SOURCE_DIR не существует"mkdir -p "$BACKUP_DIR"
# Бэкапlog "Создаю бэкап $SOURCE_DIR..."tar -czf "$ARCHIVE" "$SOURCE_DIR" 2>/dev/null
SIZE=$(du -sh "$ARCHIVE" | cut -f1)log "Готово! Архив: $ARCHIVE ($SIZE)"
# Удалить бэкапы старше 7 днейfind "$BACKUP_DIR" -name "backup_*.tar.gz" -mtime +7 -deletelog "Старые бэкапы удалены"Bash скрипты — твой первый шаг в автоматизацию. Начни с простых задач и постепенно усложняй!