Генерация таблиц прогресса
Реализация живой таблицы прогресса выполнения сприпта, обновляемой прямо в терминале - без внешних тулз, только
🛠 Скрипт:
▪️ Пояснения:
BashTex📱 #bash #utils
Реализация живой таблицы прогресса выполнения сприпта, обновляемой прямо в терминале - без внешних тулз, только
tput, trap. Звучит заманчиво.
#!/usr/bin/env bash
tasks=("Backup" "Sync configs" "Rebuild cache" "Restart services" "Cleanup")
total=${#tasks[@]}
done=0
# Убираем курсор
tput civis
# При выходе вернуть курсор
trap 'tput cnorm; echo' EXIT
# Заголовок таблицы
printf "%-20s | %-10s | %-10s\n" "Task" "Status" "Progress"
printf -- "---------------------------------------------\n"
# Печатаем пустые строки под таблицу
for ((i=0; i<total; i++)); do
printf "%-20s | %-10s | %-10s\n" "${tasks[i]}" "Pending" "0%"
done
# Запоминаем позицию курсора для обновлений
start_row=$(tput lines)
start_row=$((start_row - total))
# Функция обновления строки
update_row() {
local idx=$1
local status=$2
local progress=$3
tput cup $((start_row + idx)) 0
printf "%-20s | %-10s | %-10s\n" "${tasks[idx]}" "$status" "$progress"
}
# Основной цикл выполнения
for i in "${!tasks[@]}"; do
for p in {10..100..10}; do
update_row "$i" "Running" "${p}%"
sleep 0.1
done
update_row "$i" "Done" "100%"
done
tput cnorm
echo -e "\nВсе задачи завершены!"
tput civis / cnorm - скрывает и возвращает курсор.tput cup row col - перемещает курсор для перерисовки конкретной строки.trap '...' EXIT - гарантирует возврат нормального состояния терминала даже при Ctrl+C.printf с фиксированной шириной (%-20s) создает выровненную таблицу.BashTex
Please open Telegram to view this post
VIEW IN TELEGRAM
👍4
Please open Telegram to view this post
VIEW IN TELEGRAM
😁14🔥4🫡3
Создание песочницы для экспериментов
Иногда нужно проверить какую-то команду, поиграться с конфигами или собрать пакет - но без риска сломать систему. Для этого в linux можно сделать свою мини-песочницу, используя старое доброе
🛠 Скрипт создающий песочницу:
BashTex📱 #bash #utils
Иногда нужно проверить какую-то команду, поиграться с конфигами или собрать пакет - но без риска сломать систему. Для этого в linux можно сделать свою мини-песочницу, используя старое доброе
chroot и немного bash.chroot - это механизм, который меняет корень файловой системы для процесса. Все, что он видит, находится внутри изолированного каталога, и даже rm -rf / не затронет настоящую систему (если, конечно, настроено правильно🤓 ).
#!/usr/bin/env bash
SANDBOX="/opt/sandbox"
DEBIAN_MIRROR="http://deb.debian.org/debian"
ARCH=$(dpkg --print-architecture)
# Проверка зависимостей
for cmd in debootstrap chroot mount umount; do
command -v $cmd >/dev/null || { echo "$cmd not found"; exit 1; }
done
# Создание окружения, если нет
if [[ ! -d "$SANDBOX" ]]; then
echo "Создаю минимальную систему Debian..."
sudo debootstrap --arch="$ARCH" stable "$SANDBOX" "$DEBIAN_MIRROR"
fi
# Монтируем системные точки
sudo mount -t proc /proc "$SANDBOX/proc"
sudo mount --rbind /sys "$SANDBOX/sys"
sudo mount --rbind /dev "$SANDBOX/dev"
# Добавим базовые бинари для экспериментов
sudo cp /bin/bash "$SANDBOX/bin/"
sudo cp /usr/bin/ls "$SANDBOX/usr/bin/"
echo "Входим в песочницу!"
sudo chroot "$SANDBOX" /bin/bash
# После выхода - очистка
echo "Отмонтирую ресурсы..."
sudo umount -l "$SANDBOX/proc" "$SANDBOX/sys" "$SANDBOX/dev"
Здесь:debootstrapставит минимальный Debian прямо в каталог (/opt/sandbox)mountподключает системные псевдофайловые системы (/proc,/sys,/dev)chrootзапускает bash внутри нового корня
После выхода - все отмонтируется, и можно просто удалить/opt/sandbox
BashTex
Please open Telegram to view this post
VIEW IN TELEGRAM
👍11
Сравнение конфигураций между серверами
Иногда нужно понять, чем конфиги на dev и prod отличаются, но без полного копирования или внешних тулз.
🛠 Пример скрипта:
▪️ Как это работает
▪️ Расширение скрипта при необходимости:
1️⃣ Сравнение содержимого файлов:
2️⃣ Генерация markdown отчета:
3️⃣ Уведомление в телегу при изменениях:
BashTex📱 #bash
Иногда нужно понять, чем конфиги на dev и prod отличаются, но без полного копирования или внешних тулз.
Задача:
Сравнить/etc/nginx/и/etc/systemd/между двумя серверами, чтобы увидеть, какие файлы изменены, добавлены или удалены.
#!/usr/bin/env bash
REMOTE="user@prod.example.com"
DIRS=("/etc/nginx" "/etc/systemd")
TMPDIR="/tmp/config-compare"
LOG="/tmp/config-diff.log"
mkdir -p "$TMPDIR"
> "$LOG"
for dir in "${DIRS[@]}"; do
echo "Checking $dir ..." | tee -a "$LOG"
rsync -avz --dry-run --delete "$REMOTE:$dir/" "$dir/" \
| grep -E '^deleting|^>f' \
| sed "s|^|$dir/ |" \
>> "$TMPDIR/rsync.diff"
done
if [[ -s "$TMPDIR/rsync.diff" ]]; then
echo -e "\nDifferences found:\n" | tee -a "$LOG"
cat "$TMPDIR/rsync.diff" | tee -a "$LOG"
else
echo "Configurations match across all directories." | tee -a "$LOG"
fi
rsync --dry-run --delete - имитирует синхронизацию, но ничего не меняет, выводит список различий между каталогами.grep -E '^>f|^deleting' - фильтрует только изменения (новые или удалённые файлы).
ssh "$REMOTE" "cat /etc/nginx/nginx.conf" > /tmp/remote.conf
diff -u /etc/nginx/nginx.conf /tmp/remote.conf || echo "nginx.conf differs!"
echo -e "## Config diff report\n\`\`\`\n$(cat $TMPDIR/rsync.diff)\n\`\`\`" > /tmp/diff_report.md
[[ -s $TMPDIR/rsync.diff ]] && curl -s -F "text=$(cat $TMPDIR/rsync.diff)" \
"https://api.telegram.org/bot$TOKEN/sendMessage?chat_id=$CHAT_ID"
BashTex
Please open Telegram to view this post
VIEW IN TELEGRAM
👍8🔥2
Системные уведомления через D-Bus и bash
Bash умеет не только писать логи в консоль - он может отправлять системные уведомления прямо в графическую среду, используя D-Bus. Это хороший способ сообщить пользователю о результатах скрипта, ошибках или завершении задач, не залезая в UI.
1️⃣ Простой способ - notify-send. Самая быстрая интеграция:
Работает через D-Bus (org.freedesktop.Notifications). Можно добавить приоритет, срок жизни, категории:
2️⃣ Напрямую через dbus-send. Если notify-send недоступен (например, в минимальной среде),
можно напрямую вызвать метод D-Bus:
Выглядит громоздко, но это чистый вызов через D-Bus, минуя внешние обертки. Можно внедрить в сценарии, где нужно абсолютное управление уведомлениями.
3️⃣ Уведомления в фоновом режиме. Если скрипт работает из cron или systemd-сервиса, нужно указать сеансовую шину D-Bus пользователя. Например, так:
Это позволяет отправлять уведомления от root в сессию конкретного пользователя.
4️⃣ Практический пример
Каждое выполнение скрипта будет сопровождаться визуальными уведомлениями в системе.
▪️ Динамические уведомления. Можно обновлять уведомление (пример для GNOME/KDE, не все среды поддерживают):
BashTex📱 #bash #utils
Bash умеет не только писать логи в консоль - он может отправлять системные уведомления прямо в графическую среду, используя D-Bus. Это хороший способ сообщить пользователю о результатах скрипта, ошибках или завершении задач, не залезая в UI.
notify-send "Бэкап завершен" "Все файлы успешно сохранены" --icon=dialog-information
Работает через D-Bus (org.freedesktop.Notifications). Можно добавить приоритет, срок жизни, категории:
notify-send \
--urgency=critical \
--expire-time=10000 \
--app-name="BackupScript" \
"Ошибка резервного копирования" \
"Недостаточно места на диске!"
можно напрямую вызвать метод D-Bus:
dbus-send --session --type=method_call \
--dest=org.freedesktop.Notifications \
/org/freedesktop/Notifications \
org.freedesktop.Notifications.Notify \
string:"BashScript" \
uint32:0 \
string:"dialog-warning" \
string:"Системное уведомление" \
string:"Задача завершена с ошибками" \
array:string:"ОК" \
dict:string:string: \
int32:-1
Выглядит громоздко, но это чистый вызов через D-Bus, минуя внешние обертки. Можно внедрить в сценарии, где нужно абсолютное управление уведомлениями.
export DBUS_SESSION_BUS_ADDRESS="unix:path=/run/user/$(id -u)/bus"
notify-send "Backup completed" "Проверено $(date)"
Это позволяет отправлять уведомления от root в сессию конкретного пользователя.
#!/usr/bin/env bash
export DBUS_SESSION_BUS_ADDRESS="unix:path=/run/user/$(id -u)/bus"
TASK="Резервное копирование /home"
notify-send "Начато" "$TASK..."
if tar czf /backup/home_$(date +%F).tar.gz /home 2>/dev/null; then
notify-send "Успешно" "$TASK завершено"
else
notify-send "Ошибка" "$TASK не выполнено"
fi
Каждое выполнение скрипта будет сопровождаться визуальными уведомлениями в системе.
ID=$(notify-send "Выполняется резервное копирование..." --print-id)
sleep 5
notify-send "Завершено" "Файлы успешно сохранены" --replace-id="$ID"
BashTex
Please open Telegram to view this post
VIEW IN TELEGRAM
👍7🔥2🤨1
Вставка последнего аргумента без копипасты
Сколько раз вы выполняли команду, вроде:
а потом через секунду нужно:
и снова набираете руками путь или выделяете путь?
Есть способ не повторять последнее слово - bash это помнит.
⠀
1️⃣ Alt + . - вставка последнего аргумента
В любой момент нажмите
превратится в
Нажимайте Alt + . несколько раз, чтобы пройтись по аргументам из истории (Bash перебирает их назад).
2️⃣ Альтернатива: !$ и !. Тоже самое, но в виде подстановки из истории:
или
3️⃣ Примеры
Быстрое удаление того, что только что создали:
Скопировали и сразу зашли:
Переместили файл и открыли его в редакторе:
Копируете в несколько мест подряд:
BashTex📱 #bash #utils
Сколько раз вы выполняли команду, вроде:
cp file.txt /tmp/somedir/
а потом через секунду нужно:
cd /tmp/somedir/
и снова набираете руками путь или выделяете путь?
Есть способ не повторять последнее слово - bash это помнит.
⠀
В любой момент нажмите
Alt + . - и bash подставит последний аргумент предыдущей команды.
cp file.txt /tmp/somedir/
cd <Alt+.>
превратится в
cd /tmp/somedir/
Нажимайте Alt + . несколько раз, чтобы пройтись по аргументам из истории (Bash перебирает их назад).
cd !$
или
cd !.
!$ - последний аргумент предыдущей команды.!. - то же самое, но безопаснее (некоторые шеллы по-разному интерпретируют $).Быстрое удаление того, что только что создали:
mkdir new_dir
rm -r !$
Скопировали и сразу зашли:
cp -r project /opt/
cd !$
Переместили файл и открыли его в редакторе:
mv data.log /var/log/archive/
nano !$
Копируете в несколько мест подряд:
cp backup.tar.gz /mnt/usb/
cp !$ /srv/backups/
BashTex
Please open Telegram to view this post
VIEW IN TELEGRAM
👍14🔥2😁1
Сохраняем и восстанавливаем окружение
Со временем окружение обрастает алиасами, функциями, переменными, путями - целой экосистемой, которую легко потерять при переключении между проектами. Что если сделать снимок текущего состояния shell и потом восстановить его одной командой?
1️⃣ Сохранение окружения. Bash позволяет получить всю информацию о текущем состоянии:
Можно собрать это в файл:
Теперь у вас дамп окружения. Содержит все, что вы настроили вручную или подгрузили через .bashrc, .bash_aliases, и т.д.
2️⃣ Восстановление окружения. Чтобы применить снимок в другой сессии:
Все ваши функции, алиасы и переменные возвращаются как были.
3️⃣ Пример автоматизации. Сделаем удобный скрипт:
Использование:
4️⃣ Сравнение. Финалочка, можно сравнить два снимка:
покажет, что отличается между dev и prod.
BashTex📱 #bash #utils
Со временем окружение обрастает алиасами, функциями, переменными, путями - целой экосистемой, которую легко потерять при переключении между проектами. Что если сделать снимок текущего состояния shell и потом восстановить его одной командой?
alias # все алиасы
declare -f # все функции
declare -p # все переменные (включая окружение)
Можно собрать это в файл:
{
echo "# Snapshot from $(date)"
echo "# Aliases"
alias
echo
echo "# Functions"
declare -f
echo
echo "# Variables"
declare -p
} > ~/.bash_snapshot
Теперь у вас дамп окружения. Содержит все, что вы настроили вручную или подгрузили через .bashrc, .bash_aliases, и т.д.
source ~/.bash_snapshot
Все ваши функции, алиасы и переменные возвращаются как были.
snapshot() {
local file="${1:-~/.bash_snapshot_$(date +%F_%H-%M-%S)}"
{
alias
declare -f
declare -p | grep -v '^declare -[a-z]* BASH'
} > "$file"
echo "Saved snapshot to $file"
}
restore() {
local file="${1:-~/.bash_snapshot_latest}"
[[ -f $file ]] && source "$file" && echo "Restored from $file"
}
Использование:
snapshot ~/envs/dev.env
restore ~/envs/dev.env
diff <(grep -v '^#' dev.env) <(grep -v '^#' prod.env)
покажет, что отличается между dev и prod.
BashTex
Please open Telegram to view this post
VIEW IN TELEGRAM
👍9
Захват переменных окружения из другой сессии
Иногда нужно подцепить окружение работающего процесса: например, чтобы повторить его контекст, восстановить переменные или понять, с какими параметрами запущен сервис.
Обычно это делают через export или env, но можно достать переменные прямо из
▪️ Чтение окружения процесса. Каждый процесс в Linux хранит свои переменные в:
Это бинарный файл, где переменные разделены нулевыми байтами (\0).
Выведет все окружение процесса:
▪️ Захват и использование в текущей сессии. Если нужно подгрузить это окружение в текущий bash:
Теперь ты в том же окружении, что и процесс $PID.
▪️ Извлечение конкретной переменной. Например, достанем PATH:
А если нужно универсальнее, то функция:
▪️ Захват окружения systemd-сервисов. Systemd не всегда передает окружение дальше, но его можно вытащить по PID активного процесса:
Пример: повторить сессию nginx worker’а
Теперь любая команда (например, curl, python, php) будет запускаться с теми же переменными, что и процесс веб-сервера.
🌟 Доступно только для процессов, владельцем которых ты являешься (или root).
BashTex📱 #bash #utils
Иногда нужно подцепить окружение работающего процесса: например, чтобы повторить его контекст, восстановить переменные или понять, с какими параметрами запущен сервис.
Обычно это делают через export или env, но можно достать переменные прямо из
/proc, без доступа к shell-сессии.
/proc/<PID>/environ
Это бинарный файл, где переменные разделены нулевыми байтами (\0).
PID=1234
tr '\0' '\n' < /proc/$PID/environ
Выведет все окружение процесса:
USER=www-data
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin
PWD=/var/www
HOME=/var/www
LANG=en_US.UTF-8
PID=1234
eval "$(tr '\0' '\n' < /proc/$PID/environ | sed 's/^/export /')"
Теперь ты в том же окружении, что и процесс $PID.
Будет полезно, если:
хочешь повторить окружение демона (nginx, systemd, custom app);
нужно пересоздать контекст для отладки;
запускаешь скрипт от того же пользователя, что и процесс.
grep -z '^PATH=' /proc/$PID/environ | tr -d '\0' | cut -d= -f2-
А если нужно универсальнее, то функция:
get_env_var() {
local pid=$1 var=$2
grep -z "^${var}=" /proc/$pid/environ | tr -d '\0' | cut -d= -f2-
}
get_env_var 1234 PATH
PID=$(systemctl show -p MainPID myservice | cut -d= -f2)
tr '\0' '\n' < /proc/$PID/environ
Пример: повторить сессию nginx worker’а
PID=$(pgrep -n nginx)
eval "$(tr '\0' '\n' < /proc/$PID/environ | sed 's/^/export /')"
env | grep -E 'PATH|LANG|PWD'
Теперь любая команда (например, curl, python, php) будет запускаться с теми же переменными, что и процесс веб-сервера.
BashTex
Please open Telegram to view this post
VIEW IN TELEGRAM
👍7
Please open Telegram to view this post
VIEW IN TELEGRAM
😁21👍3
Параллельная обработка файлов без GNU Parallel
▪️ Базовый пример: запуск в фоне
Все файлы сжимаются одновременно - но это нагружает CPU. Хотелось бы ограничить количество потоков, для этого добавим контроль.
▪️ Контроль количества одновременно работающих задач
Классика для bash 4+:
▪️ Ещё лаконичнее через xargs
🌟 Работает даже в старых системах (без wait -n).
▪️ Чуть сложнее: обработка с логированием и статусом
Вывод:
Все просто, читаемо и работает без внешних зависимостей.
BashTex📱 #bash #utils
for f in *.log; do
gzip "$f" &
done
wait
Все файлы сжимаются одновременно - но это нагружает CPU. Хотелось бы ограничить количество потоков, для этого добавим контроль.
Классика для bash 4+:
MAXJOBS=4
for f in *.log; do
gzip "$f" &
(( $(jobs -r | wc -l) >= MAXJOBS )) && wait -n
done
wait
jobs -r- считает активные фоновые процессыwait -n- дожидается завершения одного из них
В итоге максимум 4 задачи одновременно
ls *.log | xargs -n1 -P4 bash -c 'gzip "$0"'
-n1 - по одному аргументу на процесс
-P4 - максимум 4 параллельных процесса
bash -c 'gzip "$0"' - шаблон выполнения
process_file() {
local f="$1"
echo "[START] $f"
sleep $((RANDOM % 5)) # эмуляция нагрузки
echo "[DONE] $f"
}
export -f process_file
ls *.log | xargs -n1 -P3 bash -c 'process_file "$0"'
Вывод:
[START] file1.log
[START] file2.log
[START] file3.log
[DONE] file2.log
[DONE] file1.log
[START] file4.log
[DONE] file3.log
[DONE] file4.log
Все просто, читаемо и работает без внешних зависимостей.
BashTex
Please open Telegram to view this post
VIEW IN TELEGRAM
👍7
Изоляция временных директорий для скриптов
Каждый серьезный скрипт должен оставлять систему чистой после работы. Для этого нужно уметь создавать, использовать и безопасно очищать временные директории, особенно если в них хранятся промежуточные файлы, ключи или результаты сборки.
Многие пишут так:
Это рискованно, потому что:
- Возможна гонка при
- Если скрипт завершится с ошибкой -
- Несколько процессов перетрут файлы друг друга
▪️ Решение: mktemp + trap
▪️ Пример: временное рабочее окружение
При любом исходе (Ctrl+C, ошибка, SIGTERM) директория будет удалена автоматически.
▪️ Уровень выше: временные файлы внутри каталога
Все временные артефакты изолированы в одной папке. После завершения скрипта следов нет.
▪️ Безопасность и права
По умолчанию
Можно задать вручную:
Полезно, если скрипт работает под рутом и обрабатывает конфиденциальные данные.
BashTex📱 #bash #utils
Каждый серьезный скрипт должен оставлять систему чистой после работы. Для этого нужно уметь создавать, использовать и безопасно очищать временные директории, особенно если в них хранятся промежуточные файлы, ключи или результаты сборки.
Многие пишут так:
TMPDIR="/tmp/mynoscript"
mkdir -p "$TMPDIR"
# ...
rm -rf "$TMPDIR"
Это рискованно, потому что:
- Возможна гонка при
mkdir в /tmp- Если скрипт завершится с ошибкой -
rm -rf не выполнится- Несколько процессов перетрут файлы друг друга
TMPDIR=$(mktemp -d -t mynoscript.XXXXXX)
trap 'rm -rf "$TMPDIR"' EXIT
mktemp -d- создает уникальную директориюtrap ... EXIT- гарантирует удаление даже при ошибках
Безопасно, изолированно и без коллизий
#!/usr/bin/env bash
set -euo pipefail
TMPDIR=$(mktemp -d -t build.XXXXXX)
trap 'echo "Cleaning $TMPDIR"; rm -rf "$TMPDIR"' EXIT
echo "Workdir: $TMPDIR"
cp -r src/* "$TMPDIR/"
pushd "$TMPDIR" >/dev/null
make all
popd >/dev/null
При любом исходе (Ctrl+C, ошибка, SIGTERM) директория будет удалена автоматически.
TMPDIR=$(mktemp -d)
trap 'rm -rf "$TMPDIR"' EXIT
LOG="$TMPDIR/run.log"
OUT="$TMPDIR/result.txt"
echo "Starting..." > "$LOG"
echo "42" > "$OUT"
Все временные артефакты изолированы в одной папке. После завершения скрипта следов нет.
По умолчанию
mktemp создает директорию с правами 700.Можно задать вручную:
TMPDIR=$(mktemp -d -p /var/tmp mynoscript.XXXXXX)
chmod 700 "$TMPDIR"
Полезно, если скрипт работает под рутом и обрабатывает конфиденциальные данные.
BashTex
Please open Telegram to view this post
VIEW IN TELEGRAM
👍8
Динамическое создание и использование временных файлов
Иногда хочется или нужно передавать данные между процессами без создания реальных временных файлов: быстро, безопасно и с контролем потока. Вот тут в дело вступают именованные каналы (named pipes, FIFO).
▪️ Пример 1: Обработка данных на лету
Здесь mkfifo создает канал, а один процесс пишет в него, пока другой читает.
Результат - никаких temp-файлов, только живой поток.
▪️ Пример 2: Конвейер с фильтрацией и tee
Это хороший способ подменить лог-файл, не записывая на диск до тех пор, пока не нужно.
▪️ Пример 3: Генерация данных и параллельная обработка
Здесь один процесс производит список файлов, а другой параллельно обрабатывает их.
Нет конфликтов записи, нет tmp-файлов, нет гонок.
🌟 Подводные камни
FIFO блокируется, если один конец не открыт: пока нет читателя, писатель висит.
При множественных писателях стоит использовать lock-механизмы (flock или temp lock-файл).
Не забывай очищать rm "$fifo", иначе в
📌 Когда это реально нужно
При распараллеливании bash-пайплайнов без промежуточных файлов;
Для асинхронных логгеров: один пишет, другой агрегирует;
Для фоново работающих демонов, взаимодействующих через потоки.
BashTex📱 #bash #utils
Иногда хочется или нужно передавать данные между процессами без создания реальных временных файлов: быстро, безопасно и с контролем потока. Вот тут в дело вступают именованные каналы (named pipes, FIFO).
Что это такое
mkfifo создает специальный файл, через который можно организовать двусторонний обмен данными между процессами, без хранения на диске. Потоки читаются и пишутся вживую, а данные не буферизуются, пока другой процесс не откроет противоположную сторону.
#!/usr/bin/env bash
pipe=$(mktemp -u) # создаем уникальное имя
mkfifo "$pipe"
# Пишем данные в FIFO в фоне
{
for i in {1..5}; do
echo "[$(date +%T)] Обработка задачи #$i"
sleep 1
done > "$pipe"
} &
# Читаем и форматируем поток
while read -r line; do
echo ">>> $line"
done < "$pipe"
rm "$pipe"
Здесь mkfifo создает канал, а один процесс пишет в него, пока другой читает.
Результат - никаких temp-файлов, только живой поток.
#!/usr/bin/env bash
fifo=$(mktemp -u)
mkfifo "$fifo"
# Пишем лог в FIFO
{
dmesg | grep "error" > "$fifo"
} &
# Читаем и одновременно сохраняем
tee /tmp/errors.log < "$fifo" | awk '{print toupper($0)}'
rm "$fifo"
Это хороший способ подменить лог-файл, не записывая на диск до тех пор, пока не нужно.
#!/usr/bin/env bash
fifo=$(mktemp -u)
mkfifo "$fifo"
producer() {
for f in *.log; do
echo "$f"
done > "$fifo"
}
consumer() {
while read -r file; do
grep "ERROR" "$file" >> errors_all.txt
done < "$fifo"
}
producer & consumer
wait
rm "$fifo"
Здесь один процесс производит список файлов, а другой параллельно обрабатывает их.
Нет конфликтов записи, нет tmp-файлов, нет гонок.
FIFO блокируется, если один конец не открыт: пока нет читателя, писатель висит.
При множественных писателях стоит использовать lock-механизмы (flock или temp lock-файл).
Не забывай очищать rm "$fifo", иначе в
/tmp может накапливаться мусор.При распараллеливании bash-пайплайнов без промежуточных файлов;
Для асинхронных логгеров: один пишет, другой агрегирует;
Для фоново работающих демонов, взаимодействующих через потоки.
BashTex
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥8
Please open Telegram to view this post
VIEW IN TELEGRAM
😁11👨💻1
Собственная корзина
С помощью bash можно написать собственную корзину и при вводе
▪️ Реализация: базовый пример
Перезапусти терминал и теперь каждый
▪️ Пример использования
А теперь:
Никаких потерь. Можно легко вернуть:
▪️ Расширение: лог и автоочистка. Добавим журнал и автоматическую очистку старых файлов:
Теперь:
Все удаления пишутся в
Старые файлы очищаются автоматически
BashTex📱 #bash #utils
С помощью bash можно написать собственную корзину и при вводе
rm будет не удаление, а перемещение файла + все это будет с датой и логом. + Немного допилив можно сделать корзину в автоочисткой через 30 дней. Будет полезно тем, кто часто удаляет нужное.Идея
Перехватываем вызов rm через алиас или функцию и вместо удаления отправляем файлы в~/.quarantine/YYYY-MM-DD/, чтобы потом можно было восстановить.
# ~/.bashrc или отдельный файл ~/.bash_safe_rm.sh
SAFE_RM_DIR="$HOME/.quarantine"
safe_rm() {
local date_dir="$SAFE_RM_DIR/$(date +%F)"
mkdir -p "$date_dir"
for file in "$@"; do
if [[ -e "$file" ]]; then
local dest="$date_dir/$(basename "$file")_$(date +%H%M%S)"
mv "$file" "$dest"
echo "Moved '$file' -> '$dest'"
else
echo "File not found: $file"
fi
done
}
alias rm='safe_rm'
Перезапусти терминал и теперь каждый
rm file.txt будет просто перемещать файл.
$ echo "test" > /tmp/test.txt
$ rm /tmp/test.txt
Moved '/tmp/test.txt' -> '/home/user/.quarantine/2025-12-02/test.txt_102355'
А теперь:
$ ls ~/.quarantine/2025-12-02/
test.txt_102355
Никаких потерь. Можно легко вернуть:
mv ~/.quarantine/2025-12-02/test.txt_102355 ~/Documents/test.txt
safe_rm() {
local date_dir="$SAFE_RM_DIR/$(date +%F)"
mkdir -p "$date_dir"
local log_file="$SAFE_RM_DIR/deleted.log"
for file in "$@"; do
if [[ -e "$file" ]]; then
local dest="$date_dir/$(basename "$file")_$(date +%H%M%S)"
mv "$file" "$dest"
echo "$(date '+%F %T') | $PWD/$file -> $dest" >> "$log_file"
echo "$file quarantined"
else
echo "File not found: $file"
fi
done
# автоочистка старше 30 дней
find "$SAFE_RM_DIR" -type d -mtime +30 -exec rm -rf {} + 2>/dev/null
}
Теперь:
Все удаления пишутся в
~/.quarantine/deleted.logСтарые файлы очищаются автоматически
BashTex
Please open Telegram to view this post
VIEW IN TELEGRAM
👍7🔥2
Реализация кэша команд
Некоторые команды тратят секунды или даже минуты, например,
Будем сохранять:
результат выполнения команды
время последнего обновления
время жизни кэша (TTL)
И в следующий раз bash просто берет результат из файла, если он не устарел, при этом экономя CPU.
🛠 Пример реализации
▪️ Пример использования
При первом вызове:
А при повторном (в течение 60 сек):
▪️ Очистка старых кэшей
Можно добавить в cron:
Или в саму функцию:
BashTex📱 #bash #utils
Некоторые команды тратят секунды или даже минуты, например,
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
Please open Telegram to view this post
VIEW IN TELEGRAM
👍9
Загрузка файлов по HTTP через /dev/tcp
Когда на сервере нет
Эта конструкция открывает сетевое соединение прямо из bash, без внешних утилит. Дальше можно писать и читать данные из этого дескриптора как из обычного файла.
▪️ Минимальный пример HTTP-запроса
скрипт откроет TCP-сокет на 80 порту,
отправит минимальный HTTP-запрос,
выведет сырые заголовки и контент ответа.
▪️ Скачивание файла с фильтрацией заголовков
Обычно хочется получить только тело, без HTTP-заголовков. Для этого можно пропустить пустую строку (\r) - границу заголовков:
▪️ HTTPS?
/dev/tcp умеет только чистый TCP, без TLS. Но можно обойтись через openssl s_client:
Да, это немного костыль, но работает даже на минимальных системах без wget/curl.
BashTex📱 #bash #utils
Когда на сервере нет
curl, wget и даже nc, а скачать файл все равно нужно, то тут на помощь приходит встроенный TCP-интерфейс: /dev/tcp/host/port. Bash поддерживает встроенные TCP/UDP-сокеты:exec {fd}>/dev/tcp/bashtex.com/80
Эта конструкция открывает сетевое соединение прямо из bash, без внешних утилит. Дальше можно писать и читать данные из этого дескриптора как из обычного файла.
#!/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"
/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
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥7👍3🗿1
Please open Telegram to view this post
VIEW IN TELEGRAM
😁15👍4
Централизованный сбор логов со всех серверов по SSH
Когда в инфраструктуре 3 и более серверов, необходимость собирать логи в одно место становится актуальной. Не всем необходимо разворачивать полноценный ELK/Graylog. Может быть достаточно скрипта, который собирает логи по SSH и приводит их к единому формату.
Сегодня покажу, как сделать легкую систему централизованного логирования на
Допустим:
Каждый наш сервер умеет отдавать свои логи через:
Мы создаем на главном сервере каталог:
И напрягаем bash забирать туда свежие логи по расписанию.
Что будем собирать:
1) systemd-журналы:
2) классические логи:
🛠 Скрипт централизованного сбора логов
-a - сохраняет структуру, права
-z - сжатие
--delete - удаляет локальные файлы, которых уже нет на сервере (чтобы каталог не рос бесконечно)
Структура в итоге выглядит так:
▪️ Версия с датой. Добавим архивирование и метку времени:
Можно затем сделать cron-задачу:
BashTex📱 #bash
Когда в инфраструктуре 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
Please open Telegram to view this post
VIEW IN TELEGRAM
👍6
Используй предстоящие новогодние выходные наилучшим образом! Изучай новые технологии или закрой пробелы в знаниях по своему стеку.
Стань экспертом в следующих направлениях:
• Системное администрирование
• Информационная безопасность
• Сетевое администрирование
• Этичный хакинг
Ссылка для своих: https://news.1rj.ru/str/admbooks
Please open Telegram to view this post
VIEW IN TELEGRAM
👍1
Локальные снапшоты каталога
Одна из интересных и полезных идей для реализации: сделать машину времени для каталога т.е. реализовать сценарий при котором возможно вернуться к состоянию вчера/час назад/неделю назад (без btrfs, zfs или lvm)
Решение: rsync + hardlinks
Это ближе всех к btrfs/ZFS-снапшотам в мире обычных файловых систем (ext4/xfs).
▪️ Структура:
Каждый каталог - полноценная копия состояния.
🛠 Мини-скрипт
▪️ Восстановление состояния каталога. Чтобы вернуть старую версию:
Либо наоборот: перейти в каталог снапшота и увидеть все как было.
▪️ Автоматизация. Например, снимок каждый час:
▪️ Полезные дополнения
1️⃣ Автопургатор (ретеншн-политика)
Удалить снапшоты старше 30 дней.
2️⃣ Игнорирование временных файлов. Создай
И вызывай rsync так:
BashTex📱 #bash #utils
Одна из интересных и полезных идей для реализации: сделать машину времени для каталога т.е. реализовать сценарий при котором возможно вернуться к состоянию вчера/час назад/неделю назад (без 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
find /backups -maxdepth 1 -type d -mtime +30 -exec rm -rf {} \;
Удалить снапшоты старше 30 дней.
.rsync-filter:
- *.tmp
- .cache/
И вызывай rsync так:
rsync -a --filter='. .rsync-filter' ...
BashTex
Please open Telegram to view this post
VIEW IN TELEGRAM
👍7