Разработка Bash-скриптов — суперважный навык для инженеров. Будь то автоматизация повторяющихся задач, объединение инструментов или управление системами, Bash всегда рядом, простой, но мощный.
Но, как и любой эффективный инструмент, он требует мастерства. В этой статье вы узнаете о 10 важнейших конструкциях Bash, используемых в реальном сценарии.
Сценарий
Представьте, что перед вами стоит задача проанализировать логовые записи сервера из нескольких файлов, извлечь неудачные попытки входа и создать отчет. Это рутинная задача, но Bash позволяет найти элегантное и многократно используемое решение.
1. Создание сцены с помощью скрипта
Начинаем выполнение поставленной задачи с написания скелета скрипта:
#!/bin/bash
set -e # Выход при условии ошибок
trap 'echo "Error on line $LINENO"; exit 1' ERR
Объяснение:
set -eгарантирует остановку скрипта при первых признаках проблемы;
trapперехватывает ошибки, предоставляя полезную отладочную информацию.
2. Обеспечение модульной организации с помощью функций
Эффективные скрипты имеют модульную организацию. Определим функцию для парсинга лог-файлов:
parse_logs() {
local file="$1"
local output="$2"
while read -r line; do
if [[ "$line" == *"FAILED LOGIN"* ]]; then
echo "$line" >> "$output"
fi
done < "$file"
}
Объяснение:
- функции делают скрипты многократно используемыми и удобными в обслуживании;
- переменные
localпредотвращают случайную перезапись.
3. Массивы: управление несколькими логами
Нам необходимо обрабатывать логи с нескольких серверов:
log_files=("server1.log" "server2.log" "server3.log")
results=()
for file in "${log_files[@]}"; do
output="${file%.log}_failed.log"
parse_logs "$file" "$output"
results+=("$output")
done
Объяснение:
- массивы помогают эффективно управлять списками элементов;
- добавляем обработанные результаты в массив для последующих действий.
4. Подстановка команд: добавление временных меток
Добавим временные метки в выходные файлы с помощью команды date:
timestamp=$(date "+%Y-%m-%d")
final_report="failed_logins_$timestamp.txt"
Объяснение:
- подстановка команд позволяет легко интегрировать динамические значения в скрипты.
5. Манипулирование строками
Перед объединением логов нужно очистить имена выходных файлов:
for file in "${results[@]}"; do
sanitized_name="${file// /_}" # Замените пробелы на символы подчеркивания
mv "$file" "$sanitized_name"
done
Объяснение:
- расширение параметров в Bash упрощает преобразование строк без использования внешних инструментов.
6. Подстановка процессов: объединение файлов
Чтобы эффективно объединить логи, выполним следующее:
cat "${results[@]}" > "$final_report"
Объяснение:
- подстановка процессов и расширение массивов позволяют быстро и эффективно работать с несколькими файлами.
7. Условная логика: настройка отчета
Настроим итоговый отчет в зависимости от его содержания:
if [[ -s "$final_report" ]]; then
echo "Report generated: $final_report"
else
echo "No failed logins found."
rm "$final_report"
fi
Объяснение:
ifобеспечивает выполнение действий, зависящих от контекста, например, от того, пуст ли отчет.
8. Оператор case: порты по умолчанию
Представьте, что вам нужно определить порты SSH и HTTPS по умолчанию в зависимости от типа сервера:
get_port() {
local server="$1"
case "$server" in
"prod"*) echo 22 ;;
"staging"*) echo 2222 ;;
*) echo 80 ;;
esac
}
Объяснение:
caseидеально подходит для элегантной работы с несколькими конкретными шаблонами.
9. Отладка с помощью set -x
Прежде чем развернуть скрипт, отладим его:
set -x # Включение отладки
# Здесь происходит запуск главного скрипта
set +x # Отключение отладки
Объяснение:
- такие инструменты отладки, как
set -x, позволяют легко отслеживать и исправлять ошибки.
10. Дескрипторы файлов для расширенного ввода-вывода
Представим, что вы читаете и обрабатываете логи из специального входного потока:
exec 3<"$final_report"
while read -u3 line; do
echo "Processed: $line"
done
exec 3<&-
Объяснение:
- дескрипторы файлов обеспечивают точный контроль над входами и выходами, позволяя выполнять параллельную обработку.
Окончательный вариант скрипта
Вот как может выглядеть готовый скрипт:
#!/bin/bash
set -e
trap 'echo "Error on line $LINENO"; exit 1' ERR
parse_logs() {
local file="$1"
local output="$2"
while read -r line; do
if [[ "$line" == *"FAILED LOGIN"* ]]; then
echo "$line" >> "$output"
fi
done < "$file"
}
log_files=("server1.log" "server2.log" "server3.log")
results=()
for file in "${log_files[@]}"; do
output="${file%.log}_failed.log"
parse_logs "$file" "$output"
results+=("$output")
done
timestamp=$(date "+%Y-%m-%d")
final_report="failed_logins_$timestamp.txt"
cat "${results[@]}" > "$final_report"
if [[ -s "$final_report" ]]; then
echo "Report generated: $final_report"
else
echo "No failed logins found."
rm "$final_report"
fi
Выводы
В этом скрипте собрано почти все, что нужно инженеру для профессионального написания Bash-скриптов: модульность, обработка ошибок, эффективная обработка данных и инструменты отладки.
Освоив эти конструкции, вы сможете не только писать лучшие скрипты, но и использовать элегантные решения для выполнения рутинных задач.
Но есть еще одна важная вещь (точнее, несколько). Далее будут представлены дополнительные конструкции, используемые мной довольно часто.
5 дополнительных конструкций Bash
Ознакомьтесь с еще 5 Bash-конструкциями, которые должен знать каждый инженер.
11. Ассоциативные массивы
Что это такое: ассоциативные массивы — пары «ключ-значение» в Bash, доступные начиная с Bash 4 и позволяющие эффективно искать и организовывать данные.
Пример: сопоставление имен серверов с их IP-адресами:
declare -A servers
servers=( ["web"]="192.168.1.10" ["db"]="192.168.1.20" ["cache"]="192.168.1.30" )
# Доступ к свойствам
echo "Web server IP: ${servers[web]}"
# Итерация по ключам
for key in "${!servers[@]}"; do
echo "$key -> ${servers[$key]}"
done
Зачем использовать ассоциативные массивы:
- для обеспечения естественного способа работы со структурированными данными без использования внешних инструментов, таких как
awkилиsed;
- для конфигураций, поиска и динамической организации данных в скриптах.
12. Heredoc-синтаксис для многострочного ввода
Что это такое: Heredoc-синтаксис позволяет вводить многострочные строки (multi-line strings) непосредственно в скрипты, улучшая читабельность при работе с шаблонами или большими объемами данных.
Пример: динамическая генерация шаблона электронной почты:
email_body=$(cat <<EOF
Hello Team,
This is a reminder for the upcoming deployment at midnight.
Regards,
DevOps
EOF)
echo "$email_body" | mail -s "Deployment Reminder" [email protected]
Зачем использовать Heredoc-синтаксис:
- для устранения необходимости в сложных конкатенациях строк или внешних файлах;
- для упрощения работы с многострочным контентом, таким как логи, шаблоны или команды, непосредственно в скрипте.
13. Команда eval для динамического выполнения команд
Что это такое: команда eval позволяет выполнить динамически созданную строку в качестве команды Bash.
Пример 1-й: выполнение команды, хранящейся в переменной:
cmd="ls -l"
eval "$cmd"
Пример 2-й: динамическое установление переменных:
var_name="greeting"
eval "$var_name='Hello, World!'"
echo "$greeting"
Зачем использовать eval:
- для обеспечения гибкости при работе с динамически генерируемыми командами или вводом;
Примечание: несмотря на эффективность этой команды, неправильное ее использование может привести к рискам безопасности при работе с недоверенными данными.
14. Подоболочки для изолированного выполнения
Что это такое: подоболочка (subshell) — дочерний процесс, в котором можно выполнять команды, не затрагивая родительскую оболочку.
Пример: выполнение команд при временном изменении каталогов:
(current_dir=$(pwd)
cd /tmp
echo "Now in $(pwd)"
)
echo "Back in $current_dir"
Зачем использовать подоболочки:
- для временного изменения переменных, сред или каталогов, не затрагивая основную оболочку;
- для выполнения изолированных операций, которые не загрязняют и не изменяют родительскую среду.
15. Именованные конвейеры (FIFO)
Что это такое: именованные конвейеры (или FIFO) — специальные файлы, которые облегчают межпроцессное взаимодействие, выступая в качестве буфера между командами.
Пример: создание именованного конвейера для передачи данных между процессами:
mkfifo my_pipe
# В одном терминале: запись в конвейер
echo "Hello from process 1" > my_pipe
# В другом терминале: чтение из конвейера
cat < my_pipe
# Очистка
rm my_pipe
Зачем использовать именованные конвейеры:
- для обеспечения асинхронной связи между процессами, позволяющей передавать данные без временных файлов;
- для сценариев обработки в реальном времени, например, для передачи логов или потоковых данных между командами.
Заключение
Дополнительные конструкции — ассоциативные массивы, heredoc-синтаксис, команда eval, подоболочки и именованные конвейеры — расширят ваш набор инструментов для написания Bash-скриптов, позволив решать более сложные задачи.
Освоив эти конструкции, вы сможете писать более элегантные, эффективные и удобные в обслуживании скрипты, предназначенные для решения реальных инженерных задач.
Читайте также:
Читайте нас в Telegram, VK и Дзен
Перевод статьи Amine Raji: 10 Bash Scripting Constructs Every Engineer Should Know




