BashTex | Linux
رفتن به کانال در Telegram
Авторский канал для тех, кто хочет глубже погрузиться в мир Linux. Подойдет для разработчиков, системных администраторов и DevOps Реклама: @dad_admin
نمایش بیشتر2 524
مشترکین
اطلاعاتی وجود ندارد24 ساعت
-67 روز
+230 روز
آرشیو پست ها
2 524
Сложные пайпы в 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 #utils2 524
Почему кубернетизация затягивается? ⚙️
Нестабильные сборки, дрейф конфигураций и неудачные чарты превращают подготовку приложения к Kubernetes в долгий и непредсказуемый процесс.
На вебинаре 23 января в 17:00 разберём, как выстроить воспроизводимый workflow и ускорить запуск приложений в кластере.
Вы узнаете:
— какие данные нужно уточнить до начала работ;
— как оптимизировать билд образов;
— какие практики использовать при подготовке Helm-чартов;
— что проверить при запуске приложения в кластере;
— какие элементы включать в ревью.
Приходите — покажем рабочие подходы, которые помогают сокращать время вывода изменений в продакшен.
👉 Зарегистрироваться
23 января в 17:00 (МСК) | Онлайн
🎁 Все участники получат практический PDF-гайд по ревью кубернетизации.
2 524
Сравнение 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 #utils2 524
Сбор данных с нескольких серверов
<(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 #utils2 524
Резервное копирование 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 📱 #bash2 524
Генерация 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 #utils2 524
Привет. Вот тебе самые топовые каналы по IT!
⚙️ Free Znanija (IT) — Самая огромная коллекция платных курсов, которые можно скачать бесплатно;
👩💻 IT Books — Самая огромная библиотека книг;
💻 Hacking & InfoSec Base — Крутой блог белого хакера;
🛡 CyberGuard — Всё про ИБ;
🤔 ИБ Вакансии — Всё, чтобы найти работу в ИБ;
👩💻 linux administration — Всё про Линукс;
👩💻 Программистика — Python, python и ещё раз python;
👩💻 GameDev Base — Всё про GameDev;
😆 //code — Самые топовые мемы по IT:
Подпишись, чтобы не потерять!
2 524
Локальные снапшоты каталога
Одна из интересных и полезных идей для реализации: сделать машину времени для каталога т.е. реализовать сценарий при котором возможно вернуться к состоянию вчера/час назад/неделю назад (без 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 #utils2 524
🤩 Admin Books —электронные книги о компьютерных технологиях.
Используй предстоящие новогодние выходные наилучшим образом! Изучай новые технологии или закрой пробелы в знаниях по своему стеку.
Стань экспертом в следующих направлениях:
• Системное администрирование
• Информационная безопасность
• Сетевое администрирование
• Этичный хакинг
Ссылка для своих: https://t.me/admbooks
2 524
Централизованный сбор логов со всех серверов по 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 📱 #bash2 524
Джуны — всё, ИИ победил. По статистике BCG, за последний год компании выбросили на улицу 75% молодняка. При этом миддлам и сеньорам зарплаты подняли в 4 (!) раза.
Чтобы подняться до уровня элит в аномально короткие сроки — подпишитесь на легендарные каналы для айтишников:
Новости и инсайды
Фронтенд разработка
ИИ и биг дата
Node js
Вёрстка
Питон и нейросети
QA-тестировщики
Там раскрыли инфу из курсов общей стоимостью 5.000.000р в эксклюзивных пошаговых инструкциях. Пока остальные пойдут на дно якорем, вы войдете в 10% тех, кто сломал систему.
2 524
Загрузка файлов по 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 #utils2 524
Есть ли в вашей команде признаки хаоса и непредсказуемости⁉️
Давайте это выясним! Для этого ответьте "Да" или "Нет":
1️⃣ Низкая эффективность спринтов. Задачи регулярно переносятся, результаты не достигаются.
2️⃣ Нет прозрачности в работе. Непонятно, на что тратится время команды и почему задачи столь ресурсозатратны.
3️⃣ Хаос в ответственности. Задачи меняют ответственных, а их поиск требует множества коммуникаций.
4️⃣ Дисбаланс нагрузки. Часть команды перегружена, другая пьет кофе.
5️⃣ Управление по устаревшим данным. Отчеты формируются не онлайн, решения принимаются на неактуальной информации.
Каждое "Да" – деньги, которые вы теряете. Вашей команде нужна Digital Q.Tasks&Teams – комплексная система управления командами и задачами с глубокой аналитикой. Она сделает результат предсказуемым.
Диасофт выпустил бесплатную версию решения – скачивайте дистрибутив базовой версии Digital Q.Tasks&Teams по ссылке
Реклама. ООО "ДИАСОФТ ЭКОСИСТЕМА". ИНН 9715403607.
2 524
Реализация кэша команд
Некоторые команды тратят секунды или даже минуты, например,
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 #utils2 524
Собственная корзина
С помощью 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 📱 #bash #utils2 524
— Ты уволен
— Почему?
— Мы оплатили GPT-5, ты больше не нужен.
Вы тоже в ах@е с такого расклада? Учился лет 5, потом столько же батрачил за копейки ради опыта — чтобы тебя заменила еб@чая железяка.
А теперь задумайтесь: нейронки развиваются с бешеной скоростью. Поэтому сейчас как никогда нужны спецы для их обучения.
Чтобы в кратчайшие сроки выйти на уровень элиты и стать топом в сфере — подпишитесь на канал @devsp. Его автор раскрывает неочевидные секреты: как в 3 месяца уместить 6 лет обучения, достойно пройти любое собеседование и что делать, чтобы лучшие компании дрались за вас.
А временами подкидывает крутые вакансии и лайфхаки, после которых вас с руками оторвут даже без опыта.
Это всё равно что встретить Нео и войти в 10% тех, кто сломал систему. Подписывайтесь: @devsp
2 524
Динамическое создание и использование временных файлов
Иногда хочется или нужно передавать данные между процессами без создания реальных временных файлов: быстро, безопасно и с контролем потока. Вот тут в дело вступают именованные каналы (named pipes, FIFO).
Что это такое mkfifo создает специальный файл, через который можно организовать двусторонний обмен данными между процессами, без хранения на диске. Потоки читаются и пишутся вживую, а данные не буферизуются, пока другой процесс не откроет противоположную сторону.▪️ Пример 1: Обработка данных на лету
#!/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-файлов, только живой поток.
▪️ Пример 2: Конвейер с фильтрацией и tee
#!/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"
Это хороший способ подменить лог-файл, не записывая на диск до тех пор, пока не нужно.
▪️ Пример 3: Генерация данных и параллельная обработка
#!/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 📱 #bash #utils
اکنون در دسترس! پژوهش تلگرام ۲۰۲۵ — مهمترین بینشهای سال 
