BashTex | Linux
2.52K subscribers
71 photos
12 videos
415 links
Авторский канал для тех, кто хочет глубже погрузиться в мир Linux.

Подойдет для разработчиков, системных администраторов и DevOps

Реклама: @dad_admin
Download Telegram
Реализация кэша команд

Некоторые команды тратят секунды или даже минуты, например, curl, find, du, git log. Если их результат не меняется часто, зачем выполнять их заново? Можно сделать кэш прямо в bash, без redis и внешних библиотек

Будем сохранять:

результат выполнения команды
время последнего обновления
время жизни кэша (TTL)

И в следующий раз bash просто берет результат из файла, если он не устарел, при этом экономя CPU.

🛠 Пример реализации


# ~/.bash_cache.sh

CACHE_DIR="$HOME/.bash_cache"
mkdir -p "$CACHE_DIR"

# $1 — время жизни (сек), $2 — команда
cache_run() {
local ttl="$1"
shift
local cmd="$*"
local key
key=$(echo "$cmd" | md5sum | awk '{print $1}')
local cache_file="$CACHE_DIR/$key.cache"
local ts_file="$CACHE_DIR/$key.ts"

# если кэш свежий — читаем
if [[ -f "$cache_file" && -f "$ts_file" ]]; then
local ts=$(<"$ts_file")
local now=$(date +%s)
if (( now - ts < ttl )); then
echo "[cache hit] $cmd"
cat "$cache_file"
return 0
fi
fi

# иначе выполняем и обновляем
echo "[cache miss] $cmd"
eval "$cmd" | tee "$cache_file"
date +%s > "$ts_file"
}


▪️ Пример использования


# Кэшируем команду на 60 секунд
cache_run 60 "curl -s https://api.github.com/repos/linux/kernel"


При первом вызове:


[cache miss] curl -s https://api.github.com/repos/linux/kernel
{ "id": 2325298, "name": "linux", ... }


А при повторном (в течение 60 сек):


[cache hit] curl -s https://api.github.com/repos/linux/kernel
{ "id": 2325298, "name": "linux", ... }


▪️ Очистка старых кэшей

Можно добавить в cron:


find "$HOME/.bash_cache" -type f -mtime +1 -delete


Или в саму функцию:


(( RANDOM % 10 == 0 )) && find "$CACHE_DIR" -type f -mtime +1 -delete &


BashTex 📱 #bash #utils
Please open Telegram to view this post
VIEW IN TELEGRAM
👍9
Загрузка файлов по HTTP через /dev/tcp

Когда на сервере нет curl, wget и даже nc, а скачать файл все равно нужно, то тут на помощь приходит встроенный TCP-интерфейс: /dev/tcp/host/port. Bash поддерживает встроенные TCP/UDP-сокеты:

exec {fd}>/dev/tcp/bashtex.com/80


Эта конструкция открывает сетевое соединение прямо из bash, без внешних утилит. Дальше можно писать и читать данные из этого дескриптора как из обычного файла.

▪️ Минимальный пример HTTP-запроса

#!/usr/bin/env bash

HOST="bashtex.com"
PATH="/index.html"

exec 3<>/dev/tcp/$HOST/80

# Отправляем HTTP-запрос
printf "GET $PATH HTTP/1.1\r\nHost: $HOST\r\nConnection: close\r\n\r\n" >&3

# Читаем ответ
while IFS= read -r line <&3; do
echo "$line"
done

exec 3>&-


скрипт откроет TCP-сокет на 80 порту,
отправит минимальный HTTP-запрос,
выведет сырые заголовки и контент ответа.

▪️ Скачивание файла с фильтрацией заголовков

Обычно хочется получить только тело, без HTTP-заголовков. Для этого можно пропустить пустую строку (\r) - границу заголовков:

#!/usr/bin/env bash

HOST="bashtex.com"
FILE="/file.txt"
OUT="file.txt"

exec 3<>/dev/tcp/$HOST/80
printf "GET $FILE HTTP/1.1\r\nHost: $HOST\r\nConnection: close\r\n\r\n" >&3

# Пропускаем заголовки
while IFS= read -r line <&3; do
[[ $line == $'\r' ]] && break
done

# Сохраняем тело
cat <&3 > "$OUT"
exec 3>&-

echo "Файл сохранён: $OUT"


▪️ HTTPS?

/dev/tcp умеет только чистый TCP, без TLS. Но можно обойтись через openssl s_client:

exec 3<> >(openssl s_client -connect bashtex.com:443 -quiet)
printf "GET / HTTP/1.1\r\nHost: bashtex.com\r\nConnection: close\r\n\r\n" >&3
cat <&3


Да, это немного костыль, но работает даже на минимальных системах без wget/curl.

BashTex 📱 #bash #utils
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥7👍3🗿1
This media is not supported in your browser
VIEW IN TELEGRAM
Сынок, ПО должно ставиться бесплатно

BashTex 📱 #юмор
Please open Telegram to view this post
VIEW IN TELEGRAM
😁19👍5
Централизованный сбор логов со всех серверов по SSH

Когда в инфраструктуре 3 и более серверов, необходимость собирать логи в одно место становится актуальной. Не всем необходимо разворачивать полноценный ELK/Graylog. Может быть достаточно скрипта, который собирает логи по SSH и приводит их к единому формату.

Сегодня покажу, как сделать легкую систему централизованного логирования на rsync + journalctl, без агентов, демонов и тяжелых сервисов.

Допустим:

Каждый наш сервер умеет отдавать свои логи через:

journalctl --since --until - системные логи
/var/log/ - классические текстовые логи

Мы создаем на главном сервере каталог:


/var/log/central/
├── server1/
├── server2/
├── server3/
└── ...


И напрягаем bash забирать туда свежие логи по расписанию.

Что будем собирать:

1) systemd-журналы:


journalctl --since "1 hour ago" --output=short-iso


2) классические логи:


/var/log/*.log
/var/log/nginx/
/var/log/syslog*
/var/log/auth.log*


🛠 Скрипт централизованного сбора логов


#!/usr/bin/env bash
set -euo pipefail

# Список серверов
SERVERS=("srv1" "srv2" "srv3")
USER="logcollector"
DEST="/var/log/central"
SINCE="1 hour ago"

mkdir -p "$DEST"

for host in "${SERVERS[@]}"; do
echo "[+] Сбор логов с $host"

HOST_DIR="$DEST/$host"
mkdir -p "$HOST_DIR"

# 1) journalctl → локальный файл
ssh "$USER@$host" \
"journalctl --since \"$SINCE\" --output=short-iso" \
> "$HOST_DIR/journal.log"

# 2) rsync логов
rsync -az --delete \
"$USER@$host:/var/log/" \
"$HOST_DIR/textlogs/"
done


ssh "journalctl …" - Мы вызываем journalctl прямо на удаленной машине и как итог лог формируется быстро и передается по SSH. Формат short-iso нужен, чтобы было удобно анализировать.

rsync -az --delete

-a - сохраняет структуру, права
-z - сжатие
--delete - удаляет локальные файлы, которых уже нет на сервере (чтобы каталог не рос бесконечно)

Структура в итоге выглядит так:


/var/log/central/srv1/
├── journal.log
└── textlogs/
├── syslog
├── auth.log
├── nginx/access.log
└── ...


▪️ Версия с датой. Добавим архивирование и метку времени:


TS=$(date +%F_%H-%M)

ssh "$USER@$host" \
"journalctl --since \"$SINCE\" --output=short-iso" \
> "$HOST_DIR/journal_$TS.log"

rsync -az "$USER@$host:/var/log/" "$HOST_DIR/textlogs_$TS/"


Можно затем сделать cron-задачу:


0 * * * * /usr/local/bin/collect-logs.sh


BashTex 📱 #bash
Please open Telegram to view this post
VIEW IN TELEGRAM
👍9
Локальные снапшоты каталога

Одна из интересных и полезных идей для реализации: сделать машину времени для каталога т.е. реализовать сценарий при котором возможно вернуться к состоянию вчера/час назад/неделю назад (без btrfs, zfs или lvm)

Решение: rsync + hardlinks

Это ближе всех к btrfs/ZFS-снапшотам в мире обычных файловых систем (ext4/xfs).

▪️ Структура:


/backups/
2025-12-05-12:00/
2025-12-05-13:00/
2025-12-05-14:00/


Каждый каталог - полноценная копия состояния.

🛠 Мини-скрипт


#!/usr/bin/env bash
set -e

SRC="/home/user/project"
DST="/backups"

TS=$(date +"%Y-%m-%d-%H:%M")
LAST="$DST/latest"
NEW="$DST/$TS"

mkdir -p "$DST"

if [[ -d "$LAST" ]]; then
echo "[i] Using hardlinks from: $LAST"
rsync -a --delete --link-dest="$LAST" "$SRC/" "$NEW/"
else
echo "[i] First snapshot — no link-dest"
rsync -a --delete "$SRC/" "$NEW/"
fi

# Update the "latest" symlink
rm -f "$LAST"
ln -s "$(basename "$NEW")" "$LAST"

echo "[i] Snapshot created: $NEW"



rsync -a —link-dest=... - команда говорит сравни каталог с предыдущим снапшотом и все неизмененные файлы сделай через hardlink. Это не копия, а именно вторая ссылка на тот же inode. Место: +0 байт.
--delete - удаленные файлы тоже исчезнут из новой точки — snapshot становится точным состоянием дерева в данный момент.
Symlink latest - позволяет быстро создавать следующую точку, не перебирая каталоги.

▪️ Восстановление состояния каталога. Чтобы вернуть старую версию:


rsync -a /backups/2025-12-05-12:00/ /home/user/project/


Либо наоборот: перейти в каталог снапшота и увидеть все как было.

▪️ Автоматизация. Например, снимок каждый час:


0 * * * * /usr/local/bin/snapshot.sh


▪️ Полезные дополнения

1️⃣ Автопургатор (ретеншн-политика)


find /backups -maxdepth 1 -type d -mtime +30 -exec rm -rf {} \;


Удалить снапшоты старше 30 дней.

2️⃣ Игнорирование временных файлов. Создай .rsync-filter:


- *.tmp
- .cache/


И вызывай rsync так:


rsync -a --filter='. .rsync-filter' ...


BashTex 📱 #bash #utils
Please open Telegram to view this post
VIEW IN TELEGRAM
👍10
Генерация unit файлов из шаблонов

Если у вас десятки сервисов, работающих по схожей схеме (например, несколько python или node-приложений), руками плодить unit-файлы в /etc/systemd/system - сомнительное удовольствие. Правильнее и логичнее будет автоматизировать путем генерирации юнитов из шаблонов через bash.

▪️ Простейший шаблон. Создаем service.tpl:


[Unit]
Description={{NAME}} service
After=network.target

[Service]
Type=simple
User={{USER}}
WorkingDirectory={{WORKDIR}}
ExecStart={{EXEC}}
Restart=always

[Install]
WantedBy=multi-user.target


▪️ Генерация через bash


#!/bin/bash
set -euo pipefail

TEMPLATE="./service.tpl"
OUTPUT_DIR="/etc/systemd/system"

generate_unit() {
local name="$1" user="$2" workdir="$3" exec="$4"
local outfile="$OUTPUT_DIR/${name}.service"

sed \
-e "s|{{NAME}}|$name|g" \
-e "s|{{USER}}|$user|g" \
-e "s|{{WORKDIR}}|$workdir|g" \
-e "s|{{EXEC}}|$exec|g" \
"$TEMPLATE" > "$outfile"

echo "Сервис $name создан: $outfile"
}



# Пример использования
generate_unit "myapp" "deploy" "/opt/myapp" "/opt/myapp/venv/bin/python app.py"

systemctl daemon-reload
systemctl enable myapp --now


▪️ Массовая генерация из списка. Храним параметры сервисов в CSV:


name,user,workdir,exec
app1,deploy,/opt/app1,/opt/app1/start.sh
app2,deploy,/opt/app2,/usr/bin/python3 /opt/app2/run.py


А bash все развернет:


#!/bin/bash
while IFS=, read -r name user workdir exec; do
[[ "$name" == "name" ]] && continue # пропускаем заголовок
generate_unit "$name" "$user" "$workdir" "$exec"
done < services.csv


BashTex 📱 #bash #utils
Please open Telegram to view this post
VIEW IN TELEGRAM
👍6🤨1
Резервное копирование Docker volumes без остановки контейнеров

Когда в контейнере крутится прод, остановка ради бэкапа будет невозможной роскошью. Но docker volumes можно сохранять, без остановки, если правильно защититься от: открытых файлов, неконсистентных записей и гонок при чтении.

1️⃣ Проблема горячих бэкапов

Если контейнер пишет в volume во время архивации:

файлы могут быть разорваны,
часть транзакций попадет в архив, а часть нет,
каталог может измениться, пока его читает tar.

Чтобы избежать этого, нужен мягкий снапшот: заморозить структуру, дождаться закрытия файлов и архивировать.

Мы это реализуем через:

проверку открытых дескрипторов lsof
копию структуры через freeze-режим
tar с фиксированным списком файлов

2️⃣ Определяем volume и контейнер


VOLUME="mydata"
BACKUP_DIR="/backups"
STAMP=$(date +%Y%m%d-%H%M)
TARGET="$BACKUP_DIR/$VOLUME-$STAMP.tar.gz"

MOUNTPOINT=$(docker volume inspect "$VOLUME" -f '{{.Mountpoint}}')


3️⃣ Ждем, пока контейнер перестанет писать. Контейнер может продолжать работать, но нам важно дождаться момента тишины.


wait_until_quiet() {
local path="$1"
local delay=${2:-1}

while lsof +D "$path" >/dev/null 2>&1; do
echo "[*] Volume busy, waiting..."
sleep "$delay"
done
}


Это не блокирует процесс, просто ждёт микропаузу между транзакциями.

4️⃣ Создаем снапшот список файлов

Важно: tar должен использовать фиксированный список, а не динамическое дерево.


SNAPLIST=$(mktemp)

find "$MOUNTPOINT" -type f -o -type d | sed "s#^$MOUNTPOINT/##" > "$SNAPLIST"


Так мы замораживаем текущее состояние структуры.

5️⃣ Архивация без остановки контейнера


echo "[*] Waiting for files to become quiet..."
wait_until_quiet "$MOUNTPOINT"

echo "[*] Creating hot-backup: $TARGET"
tar -czf "$TARGET" \
-C "$MOUNTPOINT" \
-T "$SNAPLIST"


Файлы читаются быстрее, чем контейнер успевает их поменять, а вероятность гонки почти нулевая, потому что мы:

дождались отсутствия открытых дескрипторов
зафиксировали список файлов заранее
используем tar с указанием точных путей

6️⃣ Опционально: перезапрос тишины перед чтением больших файлов

Для больших БД/логов:


pause_if_lock() {
local file="$1"
while lsof "$file" >/dev/null 2>&1; do
echo "[*] File still in use: $file"
sleep 0.5
done
}


И внутри цикла архивации:


while read -r f; do
pause_if_lock "$MOUNTPOINT/$f"
done < "$SNAPLIST"


Это максимально приближает бэкап к снапшот-принципам.

7️⃣ Очистка


rm -f "$SNAPLIST"
echo "[+] Backup completed: $TARGET"


8️⃣ Пример полностью готового скрипта. Минимальный файл backup_volume.sh:


#!/usr/bin/env bash
set -euo pipefail

VOLUME="$1"
BACKUP_DIR="/backups"
STAMP=$(date +%Y%m%d-%H%M)
TARGET="$BACKUP_DIR/$VOLUME-$STAMP.tar.gz"

MOUNTPOINT=$(docker volume inspect "$VOLUME" -f '{{.Mountpoint}}')

wait_until_quiet() {
while lsof +D "$1" >/dev/null 2>&1; do
sleep 1
done
}

echo "[*] Volume mountpoint: $MOUNTPOINT"

SNAPLIST=$(mktemp)
find "$MOUNTPOINT" -mindepth 1 -printf '%P\n' > "$SNAPLIST"

echo "[*] Waiting for quiet state..."
wait_until_quiet "$MOUNTPOINT"

echo "[*] Archiving..."
tar -czf "$TARGET" -C "$MOUNTPOINT" -T "$SNAPLIST"

rm -f "$SNAPLIST"
echo "[+] Done: $TARGET"


BashTex 📱 #bash
Please open Telegram to view this post
VIEW IN TELEGRAM
👍8
Сны при температуре 40

BashTex 📱 #юмор
Please open Telegram to view this post
VIEW IN TELEGRAM
😁15
Сбор данных с нескольких серверов


<(ssh host1 cmd) vs <(ssh host2 cmd)


Конструкция позволяет сравнивать состояние двух (или нескольких) машин без временных файлов, параллельно и максимально быстро.

Process substitution создает виртуальные файлы, в которые пишется вывод команд. SSH-запросы выполняются параллельно, а diff думает, что сравнивает два обычных файла:


diff <(ssh h1 cmd) <(ssh h2 cmd)


▪️ Лучшие практические примеры

▪️ Сравнение пакетов


diff <(ssh h1 "dpkg -l | sort") \
<(ssh h2 "dpkg -l | sort")


▪️ Конфиги (sshd_config)


diff -u <(ssh h1 "grep -v '^#' /etc/ssh/sshd_config") \
<(ssh h2 "grep -v '^#' /etc/ssh/sshd_config")


▪️ Сетевые соединения


diff <(ssh h1 "ss -tuna | sort") \
<(ssh h2 "ss -tuna | sort")


▪️ Docker-контейнеры


diff <(ssh h1 "docker ps --format '{{.Names}} {{.Image}}' | sort") \
<(ssh h2 "docker ps --format '{{.Names}} {{.Image}}' | sort")


▪️ Мини-функция


compare_hosts() {
diff <(ssh "$1" "$3") <(ssh "$2" "$3")
}


Использование:


compare_hosts node1 node2 "df -h"


BashTex 📱 #bash #utils
Please open Telegram to view this post
VIEW IN TELEGRAM
👍12
Сравнение authorized_keys между серверами

Когда в кластере несколько серверов, важно, чтобы доступ имели одни и те же пользователи с одинаковыми ключами. Несогласованность в authorized_keys будет частой точка входа для проблем.

▪️ Базовый прием: сравнение двух серверов


diff <(ssh h1 "sort ~/.ssh/authorized_keys") \
<(ssh h2 "sort ~/.ssh/authorized_keys")


Сразу видно какие ключи есть только на h1 и какие есть только на h2. Строки отличаются полностью, поэтому легко понять лишние.

▪️ Сравнение директорий для всех пользователей


compare_keys() {
user="$1"
diff <(ssh "$2" "sort /home/$user/.ssh/authorized_keys 2>/dev/null") \
<(ssh "$3" "sort /home/$user/.ssh/authorized_keys 2>/dev/null")
}


Использование:


compare_keys deploy h1 h2
compare_keys admin h1 h2


▪️ Мультисерверная проверка списка хостов


hosts=(h1 h2 h3)
base="h1"

for h in "${hosts[@]:1}"; do
echo "=== $base vs $h ==="
diff <(ssh "$base" "sort ~/.ssh/authorized_keys") \
<(ssh "$h" "sort ~/.ssh/authorized_keys")
done


Покажет отличия между эталоном и всеми остальными.

▪️ Поиск лишних ключей


ssh h1 "sort ~/.ssh/authorized_keys" \
| grep -vFf <(ssh h2 "sort ~/.ssh/authorized_keys")


Покажет ключи, присутствующие только на h1.

BashTex 📱 #bash #utils
Please open Telegram to view this post
VIEW IN TELEGRAM
👍6
Сложные пайпы в bash

Иногда нужно одновременно: подавать данные в программу, анализировать ее вывод, логировать поток, фильтровать часть данных и все это без временных файлов. Такое легко делается через tee + process substitution.

▪️ Базовый шаблон


cat input.log \
| cmd \
| tee >(grep "ERROR" > errors.log) \
| awk '{print $1, $3}'


cmd принимает stdin от cat;
tee дублирует поток: в файл-поток >(grep ...) и дальше в awk;
grep работает параллельно, не мешая основному пайпу;
и при этом нет temp-файлов, все идёт по fd

▪️ Пример 1: анализ, логирование и фильтрация сразу. Извлекаем ошибки в отдельный файл, а общий вывод обрабатываем:


journalctl -u nginx \
| tee >(grep 500 >500.log) \
| awk '/request/ {print $NF}'


▪️ Пример 2: split-поток для алертов и статистики


tail -F app.log \
| tee >(grep CRITICAL | notify-send "ALERT!") \
| awk '{count[$5]++} END {for (i in count) print i, count[i]}'


▪️ Пример 3: читаем и пишем в интерактивный процесс. Двунаправленная работа с программой:


coproc APP { cmd --interactive; }

echo "status" >&"${APP[1]}"

cat <&"${APP[0]}" \
| tee >(grep ALERT >alerts.log) \
| awk '{print "OUT:", $0}'


Работает как мини-оркестратор для интерактивного ввода/вывода.

BashTex 📱 #bash #utils
Please open Telegram to view this post
VIEW IN TELEGRAM
👍10
Использование socat как универсального канала между скриптами

socat позволяет связать любой источник данных с любым приемником. Bash-скрипты с ним превращаются в гибкие системы обмена сообщениями.

▪️ Bash через UNIX-сокет. Мини-IPC без tmp-файлов и named pipes:

Сервер:


socat UNIX-LISTEN:/tmp/app.sock,fork SYSTEM:'bash handler.sh'


Клиент:


echo "PING" | socat - UNIX-CONNECT:/tmp/app.sock


Отлично подходит для локальных API между скриптами.

2️⃣ Передача данных между серверами (TCP)

Сервер:


socat TCP-LISTEN:9000,fork FILE:/var/log/app.log


Клиент:


socat - TCP:server:9000


Можно в реальном времени стримить логи или метрики.

3️⃣ Замена сложных пайпов. Напрямую соединяем процессы, даже если они не умеют работать с stdin/stdout:


socat EXEC:"cmd1" EXEC:"cmd2"


Аналог cmd1 | cmd2, но без ограничений пайпа (например, поддержка bidirectional).

4️⃣ Двунаправленный канал в скрипте. Например, общение с интерактивным сервисом:


socat - TCP:localhost:8080 | while read l; do
echo ">> $l"
done


Можно строить собственные протоколы поверх TCP.

5️⃣ Быстрый импровизированный RPC

Сервер:


socat TCP-LISTEN:7000,reuseaddr,fork SYSTEM:'bash rpc-handler.sh'


Клиент:


echo '{"cmd":"status"}' | socat - TCP:host:7000


BashTex 📱 #bash #utils
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥5👍2
Распределенный журнал событий через SSH fan-out

Иногда нужно собрать события с группы серверов и посмотреть их как единый хронологический поток: кто что делал, когда, и на каком хосте. Можно сделать это чисто на bash + SSH.

Для этого:

По SSH запускаем команду на каждом сервере (journalctl, tail, свой лог).
Добавляем timestamp + hostname.
Объединяем и сортируем и получаем общий timeline.

▪️ Fan-out по серверам


HOSTS=(srv1 srv2 srv3)

for h in "${HOSTS[@]}"; do
ssh "$h" "journalctl -n 50 --no-pager" | \
awk -v host="$h" '{print strftime("%s"), host, $0}' &
done

wait


▪️ Централизованное объединение и сортировка


{
for h in "${HOSTS[@]}"; do
ssh "$h" "journalctl -n 200 --no-pager" \
| awk -v host="$h" '{print strftime("%s"), host, $0}'
done
} | sort -n > timeline.log


Теперь timeline.log выглядит так:


1712409123 srv1 sshd[1023]: Accepted password for admin
1712409124 srv3 systemd[1]: Started backup.
1712409125 srv2 docker[532]: Container restarted.


BashTex 📱 #bash
Please open Telegram to view this post
VIEW IN TELEGRAM
👍7
Ну уж нет

BashTex 📱 #юмор
Please open Telegram to view this post
VIEW IN TELEGRAM
😁23👍2
Ротация директорий

Когда нужно хранить несколько срезов состояния проекта или конфигов, удобно использовать ротацию директорий. Это быстрый, прозрачный и полностью файловый способ отката.

Предположим, у нас есть рабочая директория current/ и несколько ее копий:

snapshot1/ - свежий слепок
snapshot2/ - предыдущий
last-good/ - стабильная проверенная версия

Ротация выглядит так:
snapshot2 ← snapshot1 ← current

🛠 Скрипт ротации


BASE="/opt/myapp"

rotate() {
cd "$BASE" || exit 1

rm -rf snapshot2
mv snapshot1 snapshot2 2>/dev/null || true
cp -a current snapshot1

# Обновление last-good, если тесты прошли
if bash run_tests.sh; then
rm -rf last-good
cp -a current last-good
fi
}

rotate


Преимущества

cp -a быстрее архивации;
можно открыть любой снапшот обычным ls/cd;
откат в 1 команду: cp -a last-good current
работает для конфигов, кода и сервисов.

BashTex 📱 #bash
Please open Telegram to view this post
VIEW IN TELEGRAM
👍6
Параллельный запуск задач с приоритетами

Когда нужно запускать много задач, но с разным приоритетом и контролем конкуренции, можно собрать простой планировщик задач. Мы создаем: очереди: high/, normal/, low/, локи для избежания гонок, запуск задач через nice - приоритеты ядра и минимальный воркер, который берет задачи по порядку.

▪️ Структура:


queue/
high/
normal/
low/
locks/


▪️ Воркер:


pick_task() {
for q in high normal low; do
t=$(ls queue/$q | head -n1 2>/dev/null)
[ -n "$t" ] && echo "$q/$t" && return
done
}

run() {
while true; do
task=$(pick_task)
[ -z "$task" ] && sleep 1 && continue

lock="locks/$(basename "$task").lock"
( set -o noclobber; >"$lock" ) 2>/dev/null || continue

cmd=$(cat "queue/$task")
rm "queue/$task"

case $task in
high/*) nice -n -10 bash -c "$cmd" ;;
normal/*) nice -n 0 bash -c "$cmd" ;;
low/*) nice -n 10 bash -c "$cmd" ;;
esac

rm -f "$lock"
done
}

run


▪️ Добавление задачи:


echo "sleep 3 && echo DONE" > queue/high/job1


▪️ Что это дает?

- Параллельные воркеры (запусти несколько экземпляров)
- Приоритеты через nice
- Исключение гонок через простые файловые lock-и
- Очередь можно смотреть, чистить, дебажить обычными инструментами

BashTex 📱 #bash
Please open Telegram to view this post
VIEW IN TELEGRAM
👍5
Автопоиск подвисших сетевых соединений и перезапуск сервисов

Если сервис начинает подвисать, это часто видно по сети: соединения зависают в SYN-SENT, CLOSE-WAIT или сидят в одном состоянии слишком долго. Это можно определить прямо в bash, без netstat/ss.

1️⃣ Читаем таблицу соединений из /proc/net/tcp

Формат /proc/net/tcp содержит:

local addr/port
remote addr/port
state
inode (ключ!)


Мы будем искать ошибочные или зависшие состояния.



grep -E "$(echo $hung_states | sed 's/ /|/g')" /proc/net/tcp |
while read -r _ _ _ state _ _ _ _ inode _; do
echo "$inode"
done


2️⃣ Привязка inode к PID процесса. Каждый сокет отображается как /proc/$pid/fd/* socket:[inode].


find_pid_by_inode() {
inode=$1
for p in /proc/[0-9]*/fd/*; do
if readlink "$p" 2>/dev/null | grep -q "socket:\[$inode\]"; then
echo "$p" | cut -d/ -f3
fi
done
}


3️⃣ Перезапуск сервиса


for inode in $(get_hung_inodes); do
pid=$(find_pid_by_inode "$inode")
svc=$(ps -p "$pid" -o comm=)

echo "Hung socket in PID $pid ($svc)"

case "$svc" in
nginx) systemctl restart nginx ;;
sshd) systemctl restart sshd ;;
myapp) systemctl restart myapp ;;
esac
done


▪️Получаем:

- Автоматический поиск зависших TCP-соединений;
- Связь сокета с реальным процессом;
- Перезапуск только нужного сервиса;

Такой подход полезен для nginx, API-сервисов, баз данных и любых демонов, которые иногда залипают в сетевых состояниях.

BashTex 📱 #bash #utils
Please open Telegram to view this post
VIEW IN TELEGRAM
👍10
This media is not supported in your browser
VIEW IN TELEGRAM
Дедлайн Новый год стучится в дверь! 😊

Я думаю, что все устали и всем пора отдыхать, набираться сил. Все дедлайны позади, а о будущих думать пока не стоит! 😊

Я пожелаю Вам хороших каникул, счастья, здоровья, поменьше выгорания и успехов в новом году! 😊

С наступающим, 2026! 😊
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥7🫡2
Многоуровневый логинг

Большинство скриптов просто пишут все в stdout или stderr. Но bash позволяет делать полноценный логгер, разделяя потоки по уровням и управлять ими динамически во время выполнения.

Для этого, выделяем отдельные файловые дескрипторы:

3> - INFO
4> - DEBUG
5> - ERROR

Каждый можно направить в свой файл или устройство.

▪️ Базовая инициализация логгеров


LOGLEVEL="info" # debug|info|error

exec 3>info.log
exec 4>debug.log
exec 5>error.log


▪️ Лог-функции с проверкой уровня


log_info() { [[ $LOGLEVEL =~ info|debug ]] && echo "[INFO] $*" >&3; }
log_debug() { [[ $LOGLEVEL == debug ]] && echo "[DEBUG] $*" >&4; }
log_error() { echo "[ERROR] $*" >&5; }


▪️ Быстрое переключение уровня. Прямо в процессе выполнения можно менять уровень логов:


set_loglevel() {
LOGLEVEL="$1"
log_info "Loglevel switched to: $LOGLEVEL"
}

# Пример:

set_loglevel debug


▪️ Перенаправление в консоль при необходимости. Например, показывать ERROR сразу на экране:


exec 5> >(tee -a error.log >&2)


Или дебаг-режим в реальном времени:


exec 4> >(sed 's/^/[DBG]/' >&2)


BashTex 📱 #bash #utils
Please open Telegram to view this post
VIEW IN TELEGRAM
👍6
Bash-аналитика для iptables/nftables

Большинство сетевых политик обрастает десятками правил, но какие из них реально срабатывают, а какие просто мертвый груз? Разберемся и составим набор команд.

1️⃣ Статистика хитов iptables. iptables хранит счетчики прямо в правилах:


iptables -L INPUT -v -n


Поле pkts/bytes - это наши хиты.

Мини-скрипт для выборки топ-работающих правил:


iptables -L INPUT -v -n \
| awk '/ACCEPT|DROP/ {printf "%10s %s\n", $1, $0}' \
| sort -nr


Покажет самые горячие правила.

2️⃣ nftables: счетчики + export


nft list ruleset | grep -A1 'counter'


Bash-анализ:


nft list ruleset \
| awk '/counter/ {print $2, $3}' \
| sort -nr


3️⃣ Агрегация логов, какие действия реально происходят. Многие правила пишут в syslog (LOG prefix "iptables-"). Вытащим статистику по префиксам:


grep "iptables-" /var/log/syslog \
| awk '{print $NF}' \
| sort | uniq -c | sort -nr


Покажет, что именно чаще всего логирует firewall.

4️⃣ Комбинированный отчет


echo "[HIT COUNTERS]"
iptables -L INPUT -v -n | awk '/ACCEPT|DROP/ {print $1, $0}' | sort -nr | head

echo "[LOG EVENTS]"
grep "iptables-" /var/log/syslog | awk '{print $NF}' \
| sort | uniq -c | sort -nr | head


Такой небольшой пакет дает:

топ реальных срабатываний правил,
статистику нагрузочных цепочек,
понимание, что стоит оптимизировать или удалить.

BashTex 📱 #bash #utils
Please open Telegram to view this post
VIEW IN TELEGRAM
👍6