Brace expansion для массовых операций
Brace expansion - это встроенная фича, которая позволяет генерировать списки прямо в командной строке. Работает быстро, читаемо и без лишних for.
▪️ Диапазоны чисел
С шагом:
▪️ Диапазоны букв
▪️ Массовое создание файлов
Создаст:
▪️ Создание каталогов
Результат:
▪️ Комбинирование
▪️ Практика: быстрые шаблоны. Создать структуру окружений
Создать пачку логов
Удаление группы файлов
⚠️ Важный момент. Brace expansion происходит до выполнения команды. Это не цикл и не glob. Например:
сначала превращается в:
BashTex📱 #bash #utils
Brace expansion - это встроенная фича, которая позволяет генерировать списки прямо в командной строке. Работает быстро, читаемо и без лишних for.
echo {1..5}
1 2 3 4 5
С шагом:
echo {1..10..2}
1 3 5 7 9
echo {a..e}
a b c d e
touch file_{1..5}.txt
Создаст:
file_1.txt
file_2.txt
...
mkdir -p project/{src,bin,config,logs}
Результат:
project/src
project/bin
project/config
project/logs
echo {dev,prod}_{1..3}
dev_1 dev_2 dev_3 prod_1 prod_2 prod_3
mkdir -p env/{dev,stage,prod}/{logs,tmp,data}
Создать пачку логов
touch log_{2024..2026}_{01..12}.log
Удаление группы файлов
rm file_{1..10}.txt
echo file_{1..3}.txt
сначала превращается в:
echo file_1.txt file_2.txt file_3.txt
BashTex
Please open Telegram to view this post
VIEW IN TELEGRAM
👍9
Как безопасно обрабатывать файлы с пробелами в именах
Файлы с пробелами, табами и спецсимволами - классическая причина мистических багов в bash. Скрипт вроде работает… пока не встретит файл типа:
➖ Анти-паттерн
Проблема: bash режет вывод по пробелам и переводам строк. Файл
➕ Правильный способ №1 - glob + кавычки
Почему это безопаснее: * раскрывается самим shell; каждый элемент цикла - это отдельное имя файла; кавычки сохраняют пробелы.
Если нужен определённый тип:
➕ Правильный способ №2 - find -print0. Для рекурсивного обхода:
Почему это хорошо:
это безопасно даже для пробелов, табов и спецсимволов;
▪️ Безопасная передача в xargs
Пара:
BashTex📱 #bash #utils
Файлы с пробелами, табами и спецсимволами - классическая причина мистических багов в bash. Скрипт вроде работает… пока не встретит файл типа:
my file.txt или backup (old).tar.gz
for f in $(ls); do
echo "$f"
done
Проблема: bash режет вывод по пробелам и переводам строк. Файл
my file.txt превратится в два разных слова.
for f in *; do
echo "$f"
done
Почему это безопаснее: * раскрывается самим shell; каждый элемент цикла - это отдельное имя файла; кавычки сохраняют пробелы.
Если нужен определённый тип:
for f in *.log; do
[[ -e "$f" ]] || continue
echo "$f"
done
[[ -e "$f" ]] защищает, если совпадений нет.
find . -type f -print0 | while IFS= read -r -d '' f; do
echo "$f"
done
Почему это хорошо:
-print0 разделяет файлы нулевым байтом;это безопасно даже для пробелов, табов и спецсимволов;
read -d '' читает до NUL, а не до пробела.
find . -type f -print0 | xargs -0 rm -f
Пара:
-print0 и xargs -0 должны идти вместе.BashTex
Please open Telegram to view this post
VIEW IN TELEGRAM
👍7
Please open Telegram to view this post
VIEW IN TELEGRAM
😁10🗿1
Автоматическая ротация дампов БД по дням недели
Не всегда нужно хранить десятки одинаковых дампов. Для многих задач хватает схемы: 7 файлов по дням недели, где каждый новый дамп перезаписывает соответствующий день.
Идея простая:
в понедельник - backup-mon.sql
во вторник - backup-tue.sql
через неделю цикл повторяется
🛠 Пример для PostgreSQL
🛠 Пример для MySQL
▪️ Сжатие для экономии места
▪️ Cron-запуск
Каждую ночь в 02:00 дамп будет обновлять файл нужного дня.
BashTex📱 #backup
Не всегда нужно хранить десятки одинаковых дампов. Для многих задач хватает схемы: 7 файлов по дням недели, где каждый новый дамп перезаписывает соответствующий день.
Идея простая:
в понедельник - backup-mon.sql
во вторник - backup-tue.sql
через неделю цикл повторяется
#!/usr/bin/env bash
BACKUP_DIR="/var/backups/db"
DB_NAME="mydb"
DB_USER="postgres"
day=$(date +%a | tr '[:upper:]' '[:lower:]') # mon, tue, wed...
file="$BACKUP_DIR/backup-$day.sql"
mkdir -p "$BACKUP_DIR"
pg_dump -U "$DB_USER" "$DB_NAME" > "$file"
echo "Backup saved to $file"
#!/usr/bin/env bash
BACKUP_DIR="/var/backups/db"
DB_NAME="mydb"
DB_USER="root"
day=$(date +%a | tr '[:upper:]' '[:lower:]')
file="$BACKUP_DIR/backup-$day.sql"
mkdir -p "$BACKUP_DIR"
mysqldump -u "$DB_USER" "$DB_NAME" > "$file"
echo "Backup saved to $file"
mysqldump -u "$DB_USER" "$DB_NAME" | gzip > "$BACKUP_DIR/backup-$day.sql.gz"
0 2 * * * /usr/local/bin/db-backup.sh
Каждую ночь в 02:00 дамп будет обновлять файл нужного дня.
BashTex
Please open Telegram to view this post
VIEW IN TELEGRAM
👍5
Архитектура большого bash-скрипта
Пока скрипт на 30 строк - все терпимо. На 300 строк начинается хаос. На 1000 - уже никто не понимает, где init, где логика, где cleanup. Чтобы bash не превратился в лапшу, ему нужна архитектура.
📂 Нормальная структура проекта
Где:
▪️ Деление на модули
Не надо держать все в одном файле.
Лучше так:
Примеры модулей:
▪️ Naming: единый стиль
Худший вариант:
Лучше:
Для приватных функций можно префикс:
▪️ Поток исполнения. В начале файла:
Это делает скрипт читаемым как программу, а не как свалку команд.
BashTex📱 #bash #scripts
Пока скрипт на 30 строк - все терпимо. На 300 строк начинается хаос. На 1000 - уже никто не понимает, где init, где логика, где cleanup. Чтобы bash не превратился в лапшу, ему нужна архитектура.
project/
├── main.sh
├── lib/
│ ├── log.sh
│ ├── config.sh
│ ├── checks.sh
│ └── deploy.sh
├── conf/
│ └── app.conf
└── tmp/
Где:
main.sh - точка входаlib/ - функции по темамconf/ - конфигиtmp/ - временные файлыНе надо держать все в одном файле.
Лучше так:
source "$(dirname "$0")/lib/log.sh"
source "$(dirname "$0")/lib/checks.sh"
Примеры модулей:
log.sh - логгерconfig.sh - загрузка переменныхchecks.sh - проверки окруженияactions.sh - основная логикаХудший вариант:
doStuff()
x()
RunAll()
Лучше:
log_info()
check_dependencies()
deploy_app()
cleanup_tmp()
Для приватных функций можно префикс:
_internal_parse_config()
main() {
load_config
check_dependencies
run_tasks
}
main "$@"
Это делает скрипт читаемым как программу, а не как свалку команд.
BashTex
Please open Telegram to view this post
VIEW IN TELEGRAM
👍9🔥2
Быстрые текстовые трансформации в shell
Когда нужно быстро подправить текст в пайплайне, не обязательно тянуть awk или Python. Три старые утилиты часто закрывают задачу в одну строку:
cut - вырезать нужные поля
paste - склеить строки/колонки
tr - заменить или удалить символы
1️⃣ cut - достать нужный столбец. Например, из
Несколько полей:
2️⃣ tr - заменить символы. Поменять запятые на пробелы:
Удалить символы:
Сделать lowercase - uppercase:
3️⃣ paste - склеить строки в одну. Есть файл:
Сделать CSV-строку:
Результат:
Склеить два файла построчно:
▪️ Комбинируем. Из
Или список пакетов в uppercase:
BashTex📱 #bash #utils
Когда нужно быстро подправить текст в пайплайне, не обязательно тянуть awk или Python. Три старые утилиты часто закрывают задачу в одну строку:
cut - вырезать нужные поля
paste - склеить строки/колонки
tr - заменить или удалить символы
/etc/passwd взять только логины:
cut -d: -f1 /etc/passwd
-d: - разделитель-f1 - первое полеНесколько полей:
cut -d: -f1,7 /etc/passwd
echo "a,b,c" | tr ',' ' '
Удалить символы:
echo "a-b-c" | tr -d '-'
Сделать lowercase - uppercase:
echo "bash rocks" | tr '[:lower:]' '[:upper:]'
one
two
three
Сделать CSV-строку:
paste -sd, file.txt
Результат:
one,two,three
Склеить два файла построчно:
paste users.txt shells.txt
/etc/passwd достанем логины и склеим в одну строку:
cut -d: -f1 /etc/passwd | paste -sd,
Или список пакетов в uppercase:
cut -d' ' -f1 packages.txt | tr '[:lower:]' '[:upper:]'
BashTex
Please open Telegram to view this post
VIEW IN TELEGRAM
👍7🔥5
Please open Telegram to view this post
VIEW IN TELEGRAM
😁11👍2
Состояние скрипта между запусками
Многие Bash-скрипты пишутся так, будто они живут один запуск. Но в реальности скрипт могут: остановить, перезапустить, запустить второй раз параллельно или оборвать посреди обработки. Чтобы не делать все заново и не ломать данные, нужны три вещи:
lock - не дать запуститься дважды
state - помнить текущее состояние
checkpoint - знать, с какого места продолжать
1️⃣ Lock: защита от параллельного запуска
flock не даст второму экземпляру стартовать одновременно.
2️⃣ State: храним статус в файле. Например:
Или читаем:
Так можно помнить: текущий этап, последний обработанный файл и время последнего успешного шага
3️⃣ Checkpoint: продолжаем с нужного места. Допустим, обрабатываем список строк:
Если скрипт упадет на 500-й строке, следующий запуск продолжит с 501-й.
🛠 Итоговая практика
BashTex📱 #bash #scripts
Многие Bash-скрипты пишутся так, будто они живут один запуск. Но в реальности скрипт могут: остановить, перезапустить, запустить второй раз параллельно или оборвать посреди обработки. Чтобы не делать все заново и не ломать данные, нужны три вещи:
lock - не дать запуститься дважды
state - помнить текущее состояние
checkpoint - знать, с какого места продолжать
LOCK=/tmp/myjob.lock
exec 9>"$LOCK"
flock -n 9 || {
echo "Скрипт уже запущен"
exit 1
}
flock не даст второму экземпляру стартовать одновременно.
STATE=/var/tmp/myjob.state
echo "stage=download" > "$STATE"
Или читаем:
source "$STATE"
echo "$stage"
Так можно помнить: текущий этап, последний обработанный файл и время последнего успешного шага
CHECKPOINT=/var/tmp/myjob.offset
last_done=$(cat "$CHECKPOINT" 2>/dev/null || echo 0)
n=0
while IFS= read -r line; do
((n++))
(( n <= last_done )) && continue
echo "Обрабатываю: $line"
echo "$n" > "$CHECKPOINT"
done < input.txt
Если скрипт упадет на 500-й строке, следующий запуск продолжит с 501-й.
STATE_DIR=/var/tmp/myjob
mkdir -p "$STATE_DIR"
LOCK="$STATE_DIR/lock"
CHECKPOINT="$STATE_DIR/checkpoint"
exec 9>"$LOCK"
flock -n 9 || exit 1
trap 'rm -f "$LOCK"' EXIT
BashTex
Please open Telegram to view this post
VIEW IN TELEGRAM
👍11
case против if: где ветвление читается лучше
▪️ Когда лучше if. Если проверяешь: файл существует или нет, код возврата команды, число больше/меньше или несколько логических условий
Здесь if читается естественно.
▪️ Когда лучше case. Если у тебя есть один входной параметр и несколько режимов:
Для CLI это почти всегда лучше, чем цепочка:
▪️ case особенно хорош для шаблонов
if так не умеет читатьcя так же чисто.
BashTex📱 #bash
if [[ -f config.conf ]]; then
echo "Конфиг найден"
elif [[ -d config.conf ]]; then
echo "Это каталог"
else
echo "Ничего нет"
fi
Здесь if читается естественно.
cmd="$1"
case "$cmd" in
start)
echo "Запуск"
;;
stop)
echo "Остановка"
;;
restart)
echo "Перезапуск"
;;
*)
echo "Неизвестная команда"
exit 1
;;
esac
Для CLI это почти всегда лучше, чем цепочка:
if [[ "$cmd" == start ]]; then
...
elif [[ "$cmd" == stop ]]; then
...
case "$file" in
*.log) echo "Лог" ;;
*.conf) echo "Конфиг" ;;
*.sh) echo "Скрипт" ;;
esac
if так не умеет читатьcя так же чисто.
BashTex
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥6👍2
find + -exec vs xargs: где удобнее, где безопаснее
Когда нужно выполнить команду для большого количества файлов, чаще всего используют два подхода:
▪️ find -exec - простой и безопасный. Пример: удалить .log файлы старше 7 дней
Плюсы: безопасно работает с пробелами, не ломается на спецсимволах и не требует пайпа
Но есть нюанс: команда запускается для каждого файла.
▪️ Ускоренный вариант -exec. Можно запускать команду пакетами:
Теперь rm получит много файлов за раз. По скорости это почти как xargs.
▪️ xargs - быстрее для больших списков. Пример:
xargs собирает много аргументов и запускает команду одним вызовом. Можно ограничить размер батча:
⚠️ Главная проблема xargs. Файлы с пробелами ломают команду:
Поэтому правильный вариант:
Пара:
делает обработку 100% безопасной.
BashTex📱 #bash #utils
Когда нужно выполнить команду для большого количества файлов, чаще всего используют два подхода:
find ... -exec или find ... | xargs. Они решают одну задачу, но ведут себя по-разному.
find /var/log -type f -name "*.log" -mtime +7 -exec rm {} \;
{} - подставляет найденный файл.\; - завершает команду.Плюсы: безопасно работает с пробелами, не ломается на спецсимволах и не требует пайпа
Но есть нюанс: команда запускается для каждого файла.
find /var/log -type f -name "*.log" -exec rm {} +
Теперь rm получит много файлов за раз. По скорости это почти как xargs.
find /var/log -type f -name "*.log" | xargs rm
xargs собирает много аргументов и запускает команду одним вызовом. Можно ограничить размер батча:
find . -name "*.tmp" | xargs -n 10 rm
file one.txt
file two.txt
Поэтому правильный вариант:
find . -type f -print0 | xargs -0 rm
Пара:
-print0
xargs -0
делает обработку 100% безопасной.
BashTex
Please open Telegram to view this post
VIEW IN TELEGRAM
👍6
Please open Telegram to view this post
VIEW IN TELEGRAM
😁14👍1
Проверка доступности внутренних портов между серверами
Иногда сервис работает, но приложение всё равно не может подключиться. Одна из частых причин - порт недоступен между серверами: firewall, security group, routing или сервис слушает только localhost
Проверить это можно прямо из shell.
▪️ Самый простой способ - nc (netcat)
Пример вывода:
Или:
▪️ Проверка нескольких портов
▪️ Чистый bash без утилит - /dev/tcp. Мало кто знает, но bash умеет открывать TCP-соединения:
Если порт открыт - команда завершится без ошибки.
Можно сделать проверку:
▪️ Проверка списка серверов. Простой скрипт:
BashTex📱 #bash #network
Иногда сервис работает, но приложение всё равно не может подключиться. Одна из частых причин - порт недоступен между серверами: firewall, security group, routing или сервис слушает только localhost
Проверить это можно прямо из shell.
nc -zv db-server 5432
-z - проверить порт без передачи данных-v - показать результатПример вывода:
Connection to db-server 5432 port [tcp/postgresql] succeeded!
Или:
Connection refused
for port in 80 443 8080; do
nc -z host "$port" && echo "open: $port"
done
echo > /dev/tcp/db-server/5432
Если порт открыт - команда завершится без ошибки.
Можно сделать проверку:
if timeout 2 bash -c "</dev/tcp/db-server/5432"; then
echo "Порт открыт"
else
echo "Порт закрыт"
fi
servers="app1 app2 app3"
port=6379
for host in $servers; do
if nc -z "$host" "$port" 2>/dev/null; then
echo "$host OK"
else
echo "$host FAIL"
fi
done
BashTex
Please open Telegram to view this post
VIEW IN TELEGRAM
👍7
Отслеживание появления новых файлов и их автоархивация
Иногда нужно автоматически реагировать на новые файлы: сервис складывает логи, система генерирует отчеты или приложение пишет дампы Вместо cron-сканирования каталога можно использовать inotify - это механизм ядра linux для отслеживания изменений файловой системы.
▪️ Слушаем каталог. Инструмент -
🛠 Скрипт автоархивации
1. inotifywait ловит появление файла
2. имя файла передаётся в цикл
3. создается архив tar.gz
▪️ Практический кейс Приложение пишет дампы:
Скрипт автоматически делает:
Без cron и без опроса каталога.
⚠️ Важный нюанс. Иногда файл появляется до завершения записи. Лучше ловить событие close_write:
Это означает: файл закрыт после записи.
▪️ Бонус: автоудаление исходника
Каталог остается чистым.
BashTex📱 #bash #scripts
Иногда нужно автоматически реагировать на новые файлы: сервис складывает логи, система генерирует отчеты или приложение пишет дампы Вместо cron-сканирования каталога можно использовать inotify - это механизм ядра linux для отслеживания изменений файловой системы.
inotifywait (пакет inotify-tools). Простейший пример:
inotifywait -m /data/incoming -e create
-m - режим постоянного мониторинга-e create - событие создания файла
#!/usr/bin/env bash
WATCH_DIR="/data/incoming"
ARCHIVE_DIR="/data/archive"
mkdir -p "$ARCHIVE_DIR"
inotifywait -m -e create --format '%f' "$WATCH_DIR" | while read -r file; do
echo "Новый файл: $file"
tar -czf "$ARCHIVE_DIR/${file}.tar.gz" -C "$WATCH_DIR" "$file"
echo "Архив создан: ${file}.tar.gz"
done
1. inotifywait ловит появление файла
2. имя файла передаётся в цикл
3. создается архив tar.gz
dump_001.sql
dump_002.sql
dump_003.sql
Скрипт автоматически делает:
dump_001.sql.tar.gz
dump_002.sql.tar.gz
dump_003.sql.tar.gz
Без cron и без опроса каталога.
inotifywait -m -e close_write --format '%f' "$WATCH_DIR"
Это означает: файл закрыт после записи.
tar -czf "$ARCHIVE_DIR/${file}.tar.gz" -C "$WATCH_DIR" "$file" && rm "$WATCH_DIR/$file"
Каталог остается чистым.
BashTex
Please open Telegram to view this post
VIEW IN TELEGRAM
👍8
Please open Telegram to view this post
VIEW IN TELEGRAM
🫡8
Проверка, какие сервисы работают без restart-policy и почему это риск
В systemd у каждого сервиса есть параметр:
Он определяет, будет ли сервис автоматически перезапущен, если процесс упал. Если политика не задана (
▪️ Быстрая проверка одного сервиса
Пример:
или
▪️ Проверка всех сервисов. Можно быстро найти юниты без restart-policy:
Скрипт выведет сервисы, которые не перезапускаются автоматически.
▪️ Хорошие варианты restart-policy. Чаще всего используют:
или
Разница:
▪️ Пример настройки. В unit-файле:
RestartSec добавляет паузу перед перезапуском. После изменения:
BashTex📱 #systemd
В systemd у каждого сервиса есть параметр:
Restart=Он определяет, будет ли сервис автоматически перезапущен, если процесс упал. Если политика не задана (
Restart=no), сервис может просто умереть и никто его не поднимет. На продакшене это часто означает: API внезапно недоступен, очередь сообщений остановилась или воркер перестал обрабатывать задачи. И система жива, но функционально мертва.
systemctl show nginx -p Restart
Пример:
Restart=always
или
Restart=no
systemctl list-units --type=service --no-legend | awk '{print $1}' |
while read -r svc; do
policy=$(systemctl show "$svc" -p Restart --value)
[[ "$policy" == "no" ]] && echo "$svc"
done
Скрипт выведет сервисы, которые не перезапускаются автоматически.
Restart=on-failure
или
Restart=always
Разница:
on-failure - перезапуск при ошибкеalways - перезапуск всегда
[Service]
Restart=on-failure
RestartSec=5
RestartSec добавляет паузу перед перезапуском. После изменения:
systemctl daemon-reload
systemctl restart myservice
BashTex
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥6
Где остаются следы SSH-подключений
Когда вы подключаетесь к серверу по SSH, система оставляет несколько артефактов. Это полезно знать не только с точки зрения безопасности, но и для диагностики и аудита.
Разберем основные места, где фиксируются подключения.
1️⃣ /var/log/auth.log - основной лог SSH. Каждое подключение к SSH фиксируется здесь. Быстро посмотреть успешные входы:
Если система использует journald, записи можно посмотреть так:
2️⃣ /var/log/wtmp - история логинов. Это бинарный лог, где хранится информация о: входах пользователей, IP-адресах, перезагрузках и выключениях системы. Смотреть его можно командой:
Полезный вариант с расширенной информацией:
3️⃣ /var/log/lastlog - последний вход каждого пользователя. Файл хранит последнюю дату входа для каждого системного пользователя. Посмотреть:
Можно быстро увидеть: кто вообще заходил, когда был последний логин и с какого IP.
4️⃣ /var/run/utmp - текущие активные сессии. Пока пользователь подключен, информация о сессии хранится здесь. Посмотреть активные подключения:
5️⃣ /var/log/btmp - неудачные попытки входа. Если кто-то ошибся с логином или паролем, запись попадет сюда. Посмотреть:
Полезно для поиска: ошибок авторизации или сканирования SSH.
6️⃣ ~/.bash_history - история команд. Все команды пользователя обычно сохраняются в файл:
Если не хотите записывать историю в файл (например, во время диагностики), можно временно отключить:
В текущей сессии history будет работать, но после выхода команды не запишутся на диск.
⚠️ Важный момент. Если сервер отправляет логи во внешнее хранилище, то след о подключении появляется сразу в момент логина.
Поэтому в инфраструктурах часто используют: центральный syslog-сервер, systemd-journal-remote и централизованный сбор логов. Это позволяет видеть историю подключений даже если локальные логи были очищены.
BashTex📱 #security
Когда вы подключаетесь к серверу по SSH, система оставляет несколько артефактов. Это полезно знать не только с точки зрения безопасности, но и для диагностики и аудита.
Разберем основные места, где фиксируются подключения.
grep 'Accepted' /var/log/auth.log
Если система использует journald, записи можно посмотреть так:
journalctl -u ssh -r
-r покажет события в обратном порядке (сначала последние).
last
Полезный вариант с расширенной информацией:
last -Faiwx
lastlog
Можно быстро увидеть: кто вообще заходил, когда был последний логин и с какого IP.
who
#или
w
lastb
Полезно для поиска: ошибок авторизации или сканирования SSH.
~/.bash_history
Если не хотите записывать историю в файл (например, во время диагностики), можно временно отключить:
export HISTFILE=/dev/null
В текущей сессии history будет работать, но после выхода команды не запишутся на диск.
Поэтому в инфраструктурах часто используют: центральный syslog-сервер, systemd-journal-remote и централизованный сбор логов. Это позволяет видеть историю подключений даже если локальные логи были очищены.
BashTex
Please open Telegram to view this post
VIEW IN TELEGRAM
👍7🔥2
tee + process substitution: один поток и несколько получателей
Иногда нужно одновременно: увидеть вывод команды в терминале, записать его в файл и передать на обработку другой программе Если делать это по очереди - потеряется поток данных. Здесь помогает связка tee + process substitution.
▪️ Что делает tee. tee дублирует поток:
вывод остается в терминале и одновременно пишется в
▪️ Несколько получателей. tee может писать сразу в несколько файлов:
Но иногда нужно не просто файл, а другую команду.
▪️ Process substitution. Bash позволяет подставить вывод команды как файл:
Это называется process substitution.
▪️ Комбинируем
Одна копия идет в терминал, а другая в grep. Если найден ERROR, запись попадет в errors.log.
▪️ Более реальный пример. Допустим, идет сбор логов:
Теперь: полный поток остается в терминале, ошибки автоматически сохраняются
▪️ Можно делать несколько обработчиков
Один поток и сразу несколько фильтров.
BashTex📱 #bash #utils
Иногда нужно одновременно: увидеть вывод команды в терминале, записать его в файл и передать на обработку другой программе Если делать это по очереди - потеряется поток данных. Здесь помогает связка tee + process substitution.
command | tee file.log
вывод остается в терминале и одновременно пишется в
file.log
command | tee out1.log out2.log
Но иногда нужно не просто файл, а другую команду.
>(command)
Это называется process substitution.
command | tee >(grep ERROR > errors.log)
command генерирует потокtee дублирует егоОдна копия идет в терминал, а другая в grep. Если найден ERROR, запись попадет в errors.log.
journalctl -f | tee >(grep ERROR >> errors.log)
Теперь: полный поток остается в терминале, ошибки автоматически сохраняются
journalctl -f | tee \
>(grep ERROR >> errors.log) \
>(grep WARN >> warn.log)
Один поток и сразу несколько фильтров.
BashTex
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥8
Работа с exit codes: как правильно строить логику ошибок
В Bash каждая команда возвращает код завершения: 1 - успех, ≠0 - ошибка. И вся логика скрипта строится именно на этом.
▪️ Базовый принцип
Но руками
▪️ && - выполняй, если успешно
▪️ || - выполняй при ошибке
Полезно для fallback-логики.
▪️ Комбинируем
Но тут есть нюанс:
Если
Поэтому для критичной логики лучше:
▪️ set -e - автоматический выход при ошибке
Теперь скрипт завершится при любой ошибке. Но есть подводные камни:
Если grep ничего не найдёт - exit code = 1 и скрипт завершится. Иногда это нормальное поведение, а не ошибка.
▪️ Правильное использование set -e. Обычно пишут так:
Но при этом явно обрабатывают исключения:
Или:
▪️ pipefail - важно для пайпов
Без него:
С pipefail:
▪️ Общие правила:
BashTex📱 #bash
В Bash каждая команда возвращает код завершения: 1 - успех, ≠0 - ошибка. И вся логика скрипта строится именно на этом.
command
echo $?
Но руками
$? почти не используют, есть удобнее способы.
mkdir dir && cd dir
cd выполнится только если mkdir прошел успешно.
command || echo "Ошибка"
Полезно для fallback-логики.
command && echo "OK" || echo "FAIL"
Но тут есть нюанс:
Если
echo "OK" упадет (редко, но возможно), сработает || и ты получишь ложный FAIL.Поэтому для критичной логики лучше:
if command; then
echo "OK"
else
echo "FAIL"
fi
set -e
Теперь скрипт завершится при любой ошибке. Но есть подводные камни:
set -e
grep "foo" file.txt
echo "done"
Если grep ничего не найдёт - exit code = 1 и скрипт завершится. Иногда это нормальное поведение, а не ошибка.
set -euo pipefail
Но при этом явно обрабатывают исключения:
grep "foo" file.txt || true
Или:
if grep -q "foo" file.txt; then
echo "Found"
fi
set -o pipefail
Без него:
false | true
echo $? # → 0
С pipefail:
false | true
echo $? # → ошибка
&& - для цепочек успеха|| - для fallbackif - для сложной логикиset -e - осторожноpipefail - почти всегда включатьBashTex
Please open Telegram to view this post
VIEW IN TELEGRAM
👍4
Please open Telegram to view this post
VIEW IN TELEGRAM
😁13
Проверка доступности файловых систем (read/write test)
▪️ Базовая проверка записи
Если команда зависает или падает - есть проблемы с файловой системой.
▪️ Проверка с таймаутом
Теперь: если FS зависла - команда завершится по таймауту, скрипт не повиснет
▪️ Измерение latency. Можно измерить время операции:
🛠 Готовый скрипт
▪️ Проверка нескольких точек
BashTex📱 #scripts #filesystem
touch /mnt/data/.test && rm /mnt/data/.test
Если команда зависает или падает - есть проблемы с файловой системой.
timeout 3 bash -c 'touch /mnt/data/.test && rm /mnt/data/.test'
Теперь: если FS зависла - команда завершится по таймауту, скрипт не повиснет
start=$(date +%s%N)
touch /mnt/data/.test && rm /mnt/data/.test
end=$(date +%s%N)
echo $(( (end - start)/1000000 )) ms
Если операция занимает:
<10 ms - нормально
100–500 ms - уже подозрительно
секунды - проблема
#!/usr/bin/env bash
MOUNT="/mnt/data"
TIMEOUT=3
start=$(date +%s%N)
if timeout "$TIMEOUT" bash -c "touch $MOUNT/.test && rm $MOUNT/.test"; then
end=$(date +%s%N)
latency=$(( (end - start)/1000000 ))
echo "OK ($latency ms)"
else
echo "FAIL: FS недоступна или зависла"
fi
for m in /data /backup /nfs; do
echo -n "$m: "
timeout 3 bash -c "touch $m/.test && rm $m/.test" \
&& echo "OK" || echo "FAIL"
done
BashTex
Please open Telegram to view this post
VIEW IN TELEGRAM
👍4
Группировка команд {} против () - в чем реальная разница
В Bash есть два способа сгруппировать команды:
Снаружи выглядят почти одинаково. На практике - это разное поведение.
▪️ Главное отличие
Конструкция, где выполняется
▪️ Пример с переменными
Результат: 5. Теперь с ():
Результат: 0. Переменная изменилась в subshell и пропала.
▪️ Практика: перенаправления. {} удобно для группового редиректа:
Обе команды попадут в один файл.
▪️ Изоляция через (). Subshell полезен, когда нужно временно изменить окружение:
После выполнения ты останешься в текущем каталоге.
▪️ Типичный кейс
Можно заменить:
Без риска забыть вернуться обратно.
BashTex📱 #bash
В Bash есть два способа сгруппировать команды:
{ cmd1; cmd2; } и ( cmd1; cmd2 )Снаружи выглядят почти одинаково. На практике - это разное поведение.
Конструкция, где выполняется
{} - в текущем shell() - в subshell (дочернем процессе)
count=0
{ count=5; }
echo "$count"
Результат: 5. Теперь с ():
count=0
( count=5 )
echo "$count"
Результат: 0. Переменная изменилась в subshell и пропала.
{
echo "line1"
echo "line2"
} > file.txt
Обе команды попадут в один файл.
( cd /tmp && ls )
После выполнения ты останешься в текущем каталоге.
cd /tmp
do_something
cd -
Можно заменить:
( cd /tmp && do_something )
Без риска забыть вернуться обратно.
BashTex
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥6