Печать таблиц с автошириной и выравниванием
column, awk, printf - минимализм, читаемость и стиль без excel и pandas. Bash-скрипты часто выводят списки, но без форматирования:
Вывод «прыгает» - неудобно читать. Решаем с помощью:
column - автоширина и выравнивание
awk - гибкая фильтрация и формат
printf - точный контроль длины столбцов
▪️ Быстрое решение: column -t
Результат:
-t = "табличный режим", разбивает по пробелам, автоширина.
▪️ Альтернатива: awk с выравниванием
Результат:
Плюсы:
Точное выравнивание;
Подходит для чисел, выравненных по правому краю (%6s, %6d).
▪️ Автоматизация с массивом и printf
▪️ Таблица из файла CSV
-s, - задает разделитель (запятая), -t - табличный вид.
BashTex📱 #bash
column, awk, printf - минимализм, читаемость и стиль без excel и pandas. Bash-скрипты часто выводят списки, но без форматирования:
echo "user1 1234 active"
echo "user10 99 disabled"
Вывод «прыгает» - неудобно читать. Решаем с помощью:
column - автоширина и выравнивание
awk - гибкая фильтрация и формат
printf - точный контроль длины столбцов
#!/bin/bash
echo -e "USER ID STATUS\nuser1 1234 active\nuser10 99 disabled" | column -t
Результат:
USER ID STATUS
user1 1234 active
user10 99 disabled
-t = "табличный режим", разбивает по пробелам, автоширина.
awk '{ printf "%-10s %-6s %-10s\n", $1, $2, $3 }' <<< "user1 1234 active
user10 99 disabled"
Результат:
user1 1234 active
user10 99 disabled
Плюсы:
Точное выравнивание;
Подходит для чисел, выравненных по правому краю (%6s, %6d).
header=("USER" "ID" "STATUS")
rows=(
"user1 1234 active"
"user10 99 disabled"
"admin 5678 maintenance"
)
{
printf "%-10s %-6s %-12s\n" "${header[@]}"
for row in "${rows[@]}"; do
printf "%-10s %-6s %-12s\n" $row
done
} | column -t
column -s, -t < users.csv
-s, - задает разделитель (запятая), -t - табличный вид.
BashTex
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥12
Please open Telegram to view this post
VIEW IN TELEGRAM
😁20🔥1
Авторазрешение зависимостей скрипта через dynamic sourcing
В больших bash-проектах часто есть несколько вспомогательных файлов (.sh или .bash), которые содержат функции и настройки. Проблема в том, что:
📍 Скрипт может запуститься в другом каталоге - пути сломаются
📍 Некоторые модули могут отсутствовать или лежать в нестандартных местах
📍 При изменении структуры проекта придётся вручную переписывать source
Решение: динамическое подключение модулей
1️⃣ Автоматический source по списку зависимостей
Такой подход позволяет легко добавлять новые пути в поиск - модуль подтянется сам.
2️⃣ Dynamic sourcing по зависимостям из конфигурации. Можно вынести список зависимостей в отдельный файл deps.txt:
А в скрипте:
3️⃣ Поиск относительно текущего скрипта. Чтобы скрипт работал из любого места:
▪️ Плюсы подхода:
✅ Нет жестких путей;
✅ Можно запускать в разных окружениях;
✅ Удобно для open-source проектов и shared-библиотек;
✅ Легко расширять - просто добавь файл в deps.txt.
BashTex📱 #bash
В больших bash-проектах часто есть несколько вспомогательных файлов (.sh или .bash), которые содержат функции и настройки. Проблема в том, что:
Решение: динамическое подключение модулей
#!/bin/bash
set -e
# Список модулей, которые нужны для работы
modules=("logger.sh" "utils.sh" "net.sh")
# Каталоги для поиска
search_paths=(
"$PWD/lib"
"$PWD/modules"
"/usr/local/share/mynoscript"
)
for mod in "${modules[@]}"; do
found=0
for path in "${search_paths[@]}"; do
if [[ -f "$path/$mod" ]]; then
source "$path/$mod"
echo "[OK] Loaded $mod from $path"
found=1
break
fi
done
if (( !found )); then
echo "[ERROR] Module $mod not found" >&2
exit 1
fi
done
Такой подход позволяет легко добавлять новые пути в поиск - модуль подтянется сам.
logger.sh
utils.sh
net.sh
А в скрипте:
while IFS= read -r dep; do
found=0
for path in "${search_paths[@]}"; do
if [[ -f "$path/$dep" ]]; then
source "$path/$dep"
found=1
break
fi
done
(( found )) || { echo "[MISSING] $dep" >&2; exit 1; }
done < deps.txt
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
source "$SCRIPT_DIR/lib/logger.sh"
BashTex
Please open Telegram to view this post
VIEW IN TELEGRAM
👍4
Автонастройка нового сервера по шаблону
Один скрипт - и свежий VPS готов к работе.
Скрипт будет:
📍 Создать нового пользователя с sudo;
📍 Настраивать SSH-ключи и запрещать парольный вход;
📍 Отключать root-доступ;
📍 Настраивать базовый firewall (ufw);
📍 Обновлять пакеты
🛠 Сам скрипт:
▪️ Как использовать. Скопировать скрипт на новый сервер:
Запустить:
Готово. Теперь можно заходить по SSH ключом под $NEW_USER.
BashTex📱 #bash
Один скрипт - и свежий VPS готов к работе.
Скрипт будет:
#!/bin/bash
set -e
# --- Конфиг ---
NEW_USER="deploy"
SSH_KEY_URL="https://bashtex.com/id_rsa.pub"
ALLOW_PORTS=(22 80 443)
TIMEZONE="Europe/Moscow"
# --- 1. Обновление системы ---
apt update && apt upgrade -y
# --- 2. Создание пользователя ---
if ! id "$NEW_USER" &>/dev/null; then
adduser --disabled-password --gecos "" "$NEW_USER"
usermod -aG sudo "$NEW_USER"
fi
# --- 3. Установка SSH-ключа ---
mkdir -p /home/$NEW_USER/.ssh
curl -fsSL "$SSH_KEY_URL" -o /home/$NEW_USER/.ssh/authorized_keys
chmod 700 /home/$NEW_USER/.ssh
chmod 600 /home/$NEW_USER/.ssh/authorized_keys
chown -R $NEW_USER:$NEW_USER /home/$NEW_USER/.ssh
# --- 4. Настройка SSH ---
sed -i 's/^#\?PermitRootLogin .*/PermitRootLogin no/' /etc/ssh/sshd_config
sed -i 's/^#\?PasswordAuthentication .*/PasswordAuthentication no/' /etc/ssh/sshd_config
systemctl restart sshd
# --- 5. Настройка UFW ---
apt install -y ufw
ufw default deny incoming
ufw default allow outgoing
for port in "${ALLOW_PORTS[@]}"; do
ufw allow "$port"
done
ufw --force enable
# --- 6. Часовой пояс ---
timedatectl set-timezone "$TIMEZONE"
echo "[OK] Сервер настроен!"
scp setup_server.sh root@IP:/root/
Запустить:
ssh root@IP 'bash /root/setup_server.sh'
Готово. Теперь можно заходить по SSH ключом под $NEW_USER.
BashTex
Please open Telegram to view this post
VIEW IN TELEGRAM
👍10
Please open Telegram to view this post
VIEW IN TELEGRAM
😁19
Однострочники: меньше кода - быстрее результат
Bash прекрасен тем, что может решить массу задач буквально одной строкой. Подборка полезных шаблонов, которые легко адаптировать под себя.
1️⃣ Быстрый поиск файла по имени
Ищет .log файлы в системе, игнорируя ошибки доступа.
2️⃣ Топ-10 самых больших файлов
Показывает, что занимает место.
3️⃣ Проверка доступности сайта
Отдаёт HTTP-статус (200 OK, 404 Not Found и т.д.).
4️⃣ Массовое переименование файлов
Меняет расширение .txt на .bak у всех файлов в папке.
5️⃣ Замер времени выполнения команды
Показывает, сколько заняла команда.
6️⃣ Быстрый бэкап с датой
Файл будет называться
7️⃣ Мониторинг нагрузки в реальном времени
Каждую секунду показывает дисковое и RAM-состояние.
8️⃣ Удаление файлов старше N дней
Чистит
9️⃣ Загрузка и распаковка архива
Скачивает и сразу распаковывает.
1️⃣ 0️⃣ Поиск текста в файлах
Находит все упоминания ERROR в логах.
BashTex📱 #bash
Bash прекрасен тем, что может решить массу задач буквально одной строкой. Подборка полезных шаблонов, которые легко адаптировать под себя.
find / -type f -name "*.log" 2>/dev/null
Ищет .log файлы в системе, игнорируя ошибки доступа.
du -ah /path | sort -rh | head -n 10
Показывает, что занимает место.
curl -Is https://bashtex.com | head -n1
Отдаёт HTTP-статус (200 OK, 404 Not Found и т.д.).
for f in *.txt; do mv "$f" "${f%.txt}.bak"; done
Меняет расширение .txt на .bak у всех файлов в папке.
time tar -czf backup.tar.gz /data
Показывает, сколько заняла команда.
tar -czf backup-$(date +%F).tar.gz /path/to/dir
Файл будет называться
backup-2025-09-11.tar.gz.
watch -n 1 "df -h / && free -m"
Каждую секунду показывает дисковое и RAM-состояние.
find /tmp -type f -mtime +7 -delete
Чистит
/tmp от старых файлов.
curl -L https://site.ru/file.tar.gz | tar -xz
Скачивает и сразу распаковывает.
grep -R "ERROR" /var/log/
Находит все упоминания ERROR в логах.
BashTex
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥12
Однострочники: меньше кода - быстрее результат. Часть 2
Продолжение верхнего поста, но с более харкорными фишками.
1️⃣ Параллельное выполнение задач
Выполняет 4 задачи одновременно, распределяя их по входным данным.
2️⃣ Автосоздание многоуровневых JSON из списка
Превращает key=value в JSON-структуру без jq.
3️⃣ Замена текста в пачке файлов с сохранением даты
Меняет текст, но оставляет оригинальные метки времени.
4️⃣ Потоковая упаковка только изменённых файлов
Создает архив только с файлами новее указанной даты.
5️⃣ «Ловля» пакетов через tcpdump с фильтром и автопарсингом
В реальном времени выводит посещаемые хосты.
6️⃣ Горячее редактирование конфигов на удаленке
Меняет параметр в конфиге и перезапускает сервис без SCP.
7️⃣ Наблюдение за PID и его автокилл
Ждет завершения процесса и реагирует.
8️⃣ Объединение нескольких логов в один с отметкой источника
В реальном времени сливает логи с пометками.
9️⃣ Лайв-диагностика сети с графиком
Пинг с живой отрисовкой графика без промежуточных файлов.
1️⃣ 0️⃣ «Самообновляющийся» скрипт
Обновляет себя и перезапускается.
BashTex📱 #bash
Продолжение верхнего поста, но с более харкорными фишками.
seq 1 10 | xargs -n1 -P4 bash -c 'echo "Job $0"; sleep 2'
Выполняет 4 задачи одновременно, распределяя их по входным данным.
awk -F= '{gsub(/ /,"",$1); printf "\"%s\":\"%s\",\n",$1,$2}' vars.txt | sed '$ s/,$//; 1s/^/{/; $s/$/}/'
Превращает key=value в JSON-структуру без jq.
grep -rl "oldtext" . | xargs -I{} touch -r {} {}.ts && \
grep -rl "oldtext" . | xargs sed -i 's/oldtext/newtext/g' && \
for f in *.ts; do touch -r "$f" "${f%.ts}"; rm "$f"; done
Меняет текст, но оставляет оригинальные метки времени.
find /src -type f -newermt "2025-09-12" -print0 | tar --null -T - -czf patch.tar.gz
Создает архив только с файлами новее указанной даты.
tcpdump -l -i eth0 "tcp port 80" | stdbuf -oL awk '/Host:/{print $2}'
В реальном времени выводит посещаемые хосты.
ssh user@server "sed -i '/^#MaxAuthTries/s/^#//' /etc/ssh/sshd_config && systemctl restart sshd"
Меняет параметр в конфиге и перезапускает сервис без SCP.
while pidof myproc >/dev/null; do sleep 1; done && echo "Process died"
Ждет завершения процесса и реагирует.
tail -F app1.log | sed 's/^/[APP1] /' & \
tail -F app2.log | sed 's/^/[APP2] /' & wait
В реальном времени сливает логи с пометками.
ping -i 0.2 8.8.8.8 | awk -F'=' '/time=/{print $NF}' | gnuplot -p -e "plot '<cat' with lines"
Пинг с живой отрисовкой графика без промежуточных файлов.
curl -s https://bashtex.com/mynoscript.sh -o "$0" && exec bash "$0" "$@"
Обновляет себя и перезапускается.
BashTex
Please open Telegram to view this post
VIEW IN TELEGRAM
👍6🔥2
Желаю вам, чтобы Ваша зп росла быстрее, чем количество тасок. И пусть в жизни, как в коде, всегда находится правильный алгоритм!
BashTex
Please open Telegram to view this post
VIEW IN TELEGRAM
👍7🔥4😁3
Бинарные флаги и побитовые операции
Bash - не только про строки. В нtм легко использовать побитовую арифметику (&, |, ^, ~, <<, >>) для управления флагами, что бывает полезно в админских скриптах.
▪️ Базовая логика. Каждый бит числа можно трактовать как флаг (0 - выключено, 1 - включено):
▪️ Установка флага (OR |)
▪️ Проверка флага (AND &)
▪️ Сброс флага (AND + NOT &~)
▪️ Инверсия (XOR ^)
▪️ Практические кейсы
1️⃣ Система разрешений в скриптах. Можно задавать доступ к операциям через биты вместо кучи if:
2️⃣ Упаковка нескольких состояний в одно число. Например, код статуса сервиса:
3️⃣ Быстрые флаги для CLI-опций
4️⃣ Флаги как компактная замена массивов. Вместо массива enabled_features=(...) можно хранить все в одной переменной. Это особенно ценно при передаче значений между процессами.
BashTex📱 #bash #utils
Bash - не только про строки. В нtм легко использовать побитовую арифметику (&, |, ^, ~, <<, >>) для управления флагами, что бывает полезно в админских скриптах.
FLAG_READ=1 # 0001
FLAG_WRITE=2 # 0010
FLAG_EXEC=4 # 0100
FLAG_DELETE=8 # 1000
rights=0
rights=$(( rights | FLAG_READ | FLAG_WRITE ))
echo $rights # 3 (0001 + 0010 = 0011)
if (( rights & FLAG_WRITE )); then
echo "Есть право записи"
fi
rights=$(( rights & ~FLAG_READ ))
echo $rights # теперь без read
rights=$(( rights ^ FLAG_EXEC )) # переключение: если был - уберется, если не был - включится
if (( user_flags & FLAG_DELETE )); then
rm "$file"
fi
STATUS_RUNNING=1
STATUS_RESTARTING=2
STATUS_FAILED=4
status=$(( STATUS_RUNNING | STATUS_RESTARTING ))
(( status & STATUS_FAILED )) && echo "Ошибка!"
OPT_VERBOSE=1
OPT_DRYRUN=2
OPT_FORCE=4
opts=0
[[ $1 == "-v" ]] && opts=$(( opts | OPT_VERBOSE ))
[[ $1 == "-f" ]] && opts=$(( opts | OPT_FORCE ))
(( opts & OPT_VERBOSE )) && echo "[VERBOSE MODE]"
BashTex
Please open Telegram to view this post
VIEW IN TELEGRAM
👍10
Создание оффлайн инсталлятора из .deb зависимостей
Иногда сервер или рабочая машина не имеет прямого доступа в интернет. Но поставить нужный софт все же нужно. Решение: собрать локальный оффлайн инсталлятор из уже установленных пакетов и их зависимостей.
▪️ Установка dpkg-repack. На машине с интернетом:
▪️ Репак пакета в .deb. Если пакет уже стоит в системе:
▪️ Репак с зависимостями. Чтобы вытащить пакет + его зависимости:
▪️ Итог - "папка-инсталлятор". В каталоге будут все нужные .deb:
▪️ Установка на оффлайн серваке. Переносим папку, потом:
Можно превратить папку в локальный APT-репозиторий:
и подключить его через sources.list.
BashTex📱 #bash #utils
Иногда сервер или рабочая машина не имеет прямого доступа в интернет. Но поставить нужный софт все же нужно. Решение: собрать локальный оффлайн инсталлятор из уже установленных пакетов и их зависимостей.
sudo apt install dpkg-repack
dpkg-repack htop
# создаст htop_3.0.5-1_amd64.deb
mkdir offline-installer && cd offline-installer
# пример для nginx
for pkg in $(apt-cache depends --recurse --no-recommends --no-suggests \
--no-conflicts --no-breaks --no-replaces --no-enhances \
nginx | grep "^\w"); do
dpkg-repack "$pkg"
done
ls *.deb
nginx_1.24.0-1_amd64.deb
libpcre3_2:8.39-13_amd64.deb
libssl1.1_1.1.1n-0+deb11u5_amd64.deb
...
sudo dpkg -i *.deb
sudo apt -f install # подтянет зависимости из локальных .deb
Можно превратить папку в локальный APT-репозиторий:
dpkg-scanpackages . /dev/null | gzip -9c > Packages.gz
и подключить его через sources.list.
BashTex
Please open Telegram to view this post
VIEW IN TELEGRAM
👍13🔥2
Please open Telegram to view this post
VIEW IN TELEGRAM
😁18🗿2🔥1
Одновременный запуск команд на нескольких хостах
Иногда нужно быстро выполнить одну и ту же команду на нескольких серверах. Удобные тулзы для этого есть (например, pssh, ansible), но что если хочется сделать это в чистом bash, без сторонних зависимостей? Попробуем реализовать, основная идея такова:
📍 список хостов хранится в текстовом файле;
📍 скрипт пробегает по ним циклом и запускает команду через ssh;
📍 чтобы все шло параллельно - используем background (&) и ограничитель числа потоков.
🛠 Пример скрипта
▪️ Пример hosts.txt
▪️ Запуск
Вывод будет помечен [host], чтобы не путались строки.
BashTex📱 #bash
Иногда нужно быстро выполнить одну и ту же команду на нескольких серверах. Удобные тулзы для этого есть (например, pssh, ansible), но что если хочется сделать это в чистом bash, без сторонних зависимостей? Попробуем реализовать, основная идея такова:
#!/bin/bash
# parallel-ssh.sh
# Одновременное выполнение команд на хостах
HOSTFILE="hosts.txt"
CMD="$*"
MAX_PARALLEL=5 # ограничение параллельных подключений
if [[ -z "$CMD" ]]; then
echo "Использование: $0 'команда для выполнения'"
exit 1
fi
sem=0
while read -r host; do
[[ -z "$host" || "$host" =~ ^# ]] && continue
{
echo ">>> [$host]"
ssh -o BatchMode=yes -o ConnectTimeout=5 "$host" "$CMD" 2>&1 | sed "s/^/[$host] /"
} &
((sem++))
if (( sem >= MAX_PARALLEL )); then
wait -n
((sem--))
fi
done < "$HOSTFILE"
wait
server1
192.168.1.12
192.168.1.15
./parallel-ssh.sh "uptime"
Вывод будет помечен [host], чтобы не путались строки.
BashTex
Please open Telegram to view this post
VIEW IN TELEGRAM
👍9
Автоматическое формирование changelog из git-истории
Любой проект со временем обрастает десятками коммитов. Но превращать историю git log в читаемый changelog руками - занятие неблагодарное. Bash и git позволяют автоматизировать это в пару строк.
🛠 Базовый скрипт
▪️ Пример вывода
▪️ Можно также добавить:
📍 добавить дату релиза: --date=short --pretty=format:"- %s (%an, %ad)";
📍 разделить по типам коммитов (feat, fix, docs) с помощью grep;
📍 генерировать changelog только для новых коммитов с последнего релиза.
BashTex📱 #bash
Любой проект со временем обрастает десятками коммитов. Но превращать историю git log в читаемый changelog руками - занятие неблагодарное. Bash и git позволяют автоматизировать это в пару строк.
#!/bin/bash
# gen-changelog.sh
OUTFILE="CHANGELOG.md"
{
echo "# Changelog"
echo
# Перебираем теги в обратном порядке
for tag in $(git tag --sort=-creatordate); do
prev=$(git describe --tags --abbrev=0 "$tag"^ 2>/dev/null || echo "")
echo "## $tag"
echo
if [[ -n "$prev" ]]; then
range="$prev..$tag"
else
range="$tag"
fi
git log --pretty=format:"- %s (%an)" "$range"
echo -e "\n"
done
} > "$OUTFILE"
echo "Changelog обновлен: $OUTFILE"
# Changelog
## v2.2.0
- Добавлены новые баги (Ivan)
- Исправлен баг с путями (Anna)
## v2.1.0
- Рефакторинг логики загрузки (Ivan)
- Улучшена работа с сетью (Petr)
BashTex
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥8
Скрипт для массового обновления SSH-ключей пользователей на кластере
Когда у вас десятки серверов и несколько пользователей, ручное обновление
🛠 Пример скрипта
▪️ Что делает скрипт
📍 перебирает все хосты из hosts.txt;
📍 для каждого пользователя заливает новый authorized_keys;
📍 выставляет правильные права (700 для .ssh, 600 для ключа);
📍 пишет лог успешных и неудачных обновлений.
BashTex📱 #bash
Когда у вас десятки серверов и несколько пользователей, ручное обновление
~/.ssh/authorized_keys превращается в кошмар. Чтобы централизованно обновить ключи (например, при ротации), скрипт может взять все под контроль.Наш скрипт будет:📍 Хранить эталонные ключи пользователей в центральном репозитории (например, /srv/ssh-keys/ или в Git).📍 Проходить по списку хостов и обновлять authorized_keys.📍 Логировать успехи и ошибки.
#!/bin/bash
HOSTS="hosts.txt" # список серверов: один hostname/ip на строку
USERS="users.txt" # список пользователей: один user на строку
KEYDIR="/srv/ssh-keys" # где лежат эталонные ключи (user.pub)
LOGFILE="ssh_update.log"
while read -r host; do
echo "[*] Обновляем ключи на $host"
for user in $(cat "$USERS"); do
keyfile="$KEYDIR/$user.pub"
if [[ -f "$keyfile" ]]; then
ssh "$host" "mkdir -p /home/$user/.ssh && \
chmod 700 /home/$user/.ssh && \
cat > /home/$user/.ssh/authorized_keys && \
chmod 600 /home/$user/.ssh/authorized_keys && \
chown -R $user:$user /home/$user/.ssh" \
< "$keyfile"
if [[ $? -eq 0 ]]; then
echo "$(date '+%F %T') OK $user@$host" >> "$LOGFILE"
else
echo "$(date '+%F %T') FAIL $user@$host" >> "$LOGFILE"
fi
else
echo "Нет ключа для $user"
fi
done
done < "$HOSTS"
BashTex
Please open Telegram to view this post
VIEW IN TELEGRAM
👍11
Использование coproc для работы с асинхронными потоками
Многие знают про & и wait, но есть куда более элегантный инструмент для асинхронной работы -
▪️ Простейший пример
Процесс bc живет, пока вы его не закроете. То есть можно посылать команды и читать ответы несколько раз.
▪️ Практические кейсы
📍 Асинхронные задачи с обратной связью. Можно запускать ping, tcpdump или tail -f в coproc, а потом считывать поток строк построчно:
📍 Фоновый обработчик. Например, пишем строки в процессинг-скрипт:
📍 Несколько процессов. Coproc можно называть:
BashTex📱 #bash #utils
Многие знают про & и wait, но есть куда более элегантный инструмент для асинхронной работы -
coproc. Это встроенная команда, которая запускает процесс в фоне и автоматически подключает к нему двусторонний канал (pipe).❓ Как это работает
coproc создает процесс, у которого:
stdin доступен через дескриптор ${COPROC[1]} (запись в процесс);
stdout доступен через ${COPROC[0]} (чтение из процесса);
имя coproc можно задавать, чтобы управлять несколькими одновременно.
#!/bin/bash
# Запускаем фоновый bc как "асинхронный калькулятор"
coproc CALC { bc -l; }
# Отправляем в stdin команды
echo "2+3" >&"${CALC[1]}"
echo "s(1)" >&"${CALC[1]}"
# Читаем ответы из stdout
read -u "${CALC[0]}" result1
read -u "${CALC[0]}" result2
echo "Результат 1: $result1"
echo "Результат 2: $result2"
Процесс bc живет, пока вы его не закроете. То есть можно посылать команды и читать ответы несколько раз.
coproc PINGER { ping -O 8.8.8.8; }
while read -ru "${PINGER[0]}" line; do
echo "PING: $line"
done
coproc HANDLER { while read line; do echo ">> $line"; done; }
echo "Hello" >&"${HANDLER[1]}"
echo "World" >&"${HANDLER[1]}"
coproc P1 { ping -c2 8.8.8.8; }
coproc P2 { ping -c2 1.1.1.1; }
▪️ Подводные камни
Каналы буферизуются, иногда нужно sleep или stdbuf -oL для построчной работы.
Закрывайте дескрипторы exec {fd}>&-, иначе процесс может висеть.
coproc работает только в bash ≥ 4.
BashTex
Please open Telegram to view this post
VIEW IN TELEGRAM
👍7
Встраивание мини-HTTP-сервера на netcat + bash (для локальных API)
Иногда хочется быстро поднять легковесный API без Nginx/Apache, чтобы протестировать интеграцию или выдавать системные данные в JSON. Для этого достаточно bash + netcat.
🛠 Минимальный пример
▪️ Запуск
Теперь можно открыть в браузере: http://localhost:8080
▪️ Возможности
📍 отдавать системные метрики:
📍 простой healthcheck для Docker:
📍 мини-API для локальных скриптов (например, статус бэкапа).
Такой подход позволяет собрать сверхлегкий REST-like API прямо из bash - без сторонних веб-фреймворков.
BashTex📱 #bash #utils
Иногда хочется быстро поднять легковесный API без Nginx/Apache, чтобы протестировать интеграцию или выдавать системные данные в JSON. Для этого достаточно bash + netcat.
Общая идея такова:📍 netcat слушает порт (например, 8080);📍 bash скрипт парсит запрос и отвечает заголовками + данными;📍 результат можно использовать для локальных API-запросов (например, мониторинг).
#!/bin/bash
PORT=8080
while true; do
# Принимаем одно соединение
{
# Читаем первую строку HTTP-запроса
read request
echo ">>> $request"
# Отправляем HTTP-ответ
echo -e "HTTP/1.1 200 OK\r"
echo -e "Content-Type: application/json\r"
echo -e "\r"
echo -e '{"status": "ok", "time": "'$(date +%T)'"}'
} | nc -l -p $PORT -q 1
done
chmod +x mini-http.sh
./mini-http.sh
Теперь можно открыть в браузере: http://localhost:8080
echo -e '{"load": "'$(uptime | awk "{print \$10}")'"}'
curl localhost:8080/healthТакой подход позволяет собрать сверхлегкий REST-like API прямо из bash - без сторонних веб-фреймворков.
BashTex
Please open Telegram to view this post
VIEW IN TELEGRAM
👍7🔥5
Please open Telegram to view this post
VIEW IN TELEGRAM
😁38👍5🔥1🫡1
Автоматическое создание LVM-снапшотов с очисткой старых копий
LVM (Logical Volume Manager) - это не только удобное управление дисковыми разделами, но и инструмент для снапшотов. С их помощью можно «заморозить» состояние тома, сделать бэкап или провести тестовые изменения. Но если такие снапшоты создавать регулярно, нужно и контролировать их «уборку».
🛠 Пример скрипта
▪️ Как использовать
Положите скрипт, например в
Сделайте его исполняемым:
Добавьте в cron, например, раз в день в 02:00:
BashTex📱 #bash
LVM (Logical Volume Manager) - это не только удобное управление дисковыми разделами, но и инструмент для снапшотов. С их помощью можно «заморозить» состояние тома, сделать бэкап или провести тестовые изменения. Но если такие снапшоты создавать регулярно, нужно и контролировать их «уборку».
#!/bin/bash
VG="vg0" # имя volume group
LV="data" # исходный LV
SNAP_SIZE="2G" # размер снапшота
KEEP=5 # сколько последних снапшотов хранить
DATE=$(date +%Y%m%d-%H%M)
SNAP_NAME="${LV}_snap_${DATE}"
# 1. Создаём снапшот
lvcreate -L "$SNAP_SIZE" -s -n "$SNAP_NAME" "/dev/$VG/$LV"
echo "[+] Snapshot создан: $SNAP_NAME"
# 2. Список снапшотов, сортировка по дате
SNAPS=$(lvs --noheadings -o lv_name $VG | grep "${LV}_snap_" | sort)
# 3. Если снапшотов больше $KEEP - удаляем старые
COUNT=$(echo "$SNAPS" | wc -l)
if (( COUNT > KEEP )); then
REMOVE=$(echo "$SNAPS" | head -n $((COUNT - KEEP)))
for s in $REMOVE; do
echo "[-] Удаляем старый снапшот: $s"
lvremove -f "/dev/$VG/$s"
done
fi
Положите скрипт, например в
/usr/local/bin/auto-lvm-snapshot.shСделайте его исполняемым:
chmod +x /usr/local/bin/auto-lvm-snapshot.sh
Добавьте в cron, например, раз в день в 02:00:
0 2 * * * root /usr/local/bin/auto-lvm-snapshot.sh >> /var/log/lvm-snapshot.log 2>&1
▪️ Важные моменты📍 Снапшоты занимают место: при изменении данных в оригинальном LV они растут. Размер -L должен быть с запасом.📍 Если снапшот «переполнится» - он станет невалидным.📍 Для бэкапа можно монтировать снапшот в отдельный каталог, а после копирования - удалять его.
BashTex
Please open Telegram to view this post
VIEW IN TELEGRAM
👍6
Продвинутые regex выражения и их подводные камни
В bash оператор [[ string =~ regex ]] дает инструмент для проверки строк по регулярным выражениям. Но у него есть особенности, о которых часто забывают.
▪️ Базовый синтаксис
=~ - оператор сравнения по регулярному выражению (ERE)
без кавычек вокруг regex (!), иначе он станет строкой
▪️ Подводные камни
1️⃣ Кавычки ломают regex
2️⃣ Пробелы в regex → нужно экранировать
3️⃣ Группы и BASH_REMATCH
BASH_REMATCH[0] - вся строка, [1]..[n] — группы.
4️⃣ Regex всегда интерпретируется как ERE (Extended Regex)
+, ?, | работают без \
Но нет поддержки PCRE (\d, \w, lookahead и т.п.)
5️⃣ Неочевидные совпадения из-за пустого паттерна
6️⃣ Вложенные переменные в regex. Если подставлять переменную в regex, лучше использовать () для контроля:
▪️ Полезные приемы
1️⃣ Проверка на число:
2️⃣ Проверка на IPv4:
3️⃣ Проверка на slug (латиница, цифры, дефис):
BashTex📱 #bash
В bash оператор [[ string =~ regex ]] дает инструмент для проверки строк по регулярным выражениям. Но у него есть особенности, о которых часто забывают.
str="user123"
if [[ $str =~ ^user[0-9]+$ ]]; then
echo "Совпало"
else
echo "Нет совпадения"
fi
=~ - оператор сравнения по регулярному выражению (ERE)
без кавычек вокруг regex (!), иначе он станет строкой
[[ $str =~ "^[0-9]+$" ]] # НЕ работает как regex
[[ $str =~ ^[0-9]+$ ]] # правильно
[[ "foo bar" =~ foo\ bar ]] # правильно
str="id=42 user=admin"
if [[ $str =~ id=([0-9]+)\ user=([a-z]+) ]]; then
echo "ID: ${BASH_REMATCH[1]}"
echo "User: ${BASH_REMATCH[2]}"
fi
BASH_REMATCH[0] - вся строка, [1]..[n] — группы.
+, ?, | работают без \
Но нет поддержки PCRE (\d, \w, lookahead и т.п.)
regex="^$" # пустая строка
[[ "abc" =~ $regex ]] && echo "совпало" # неожиданно не совпадает
re="[0-9]{2}"
[[ "2025" =~ ($re) ]] && echo "нашли: ${BASH_REMATCH[1]}"
[[ $x =~ ^[0-9]+$ ]] && echo "число"
[[ $ip =~ ^([0-9]{1,3}\.){3}[0-9]{1,3}$ ]] && echo "ipv4"
[[ $slug =~ ^[a-z0-9-]+$ ]] || echo "некорректный slug"
BashTex
Please open Telegram to view this post
VIEW IN TELEGRAM
👍7
Оптимизация чтения больших файлов
Когда работаешь с большими логами (гигабайты), вопрос «как читать быстрее и без лишнего расхода памяти?» становится критичным. Сегодня рассмотрим два подхода:
▪️ Подход 1: Чтение чанками (построчно или блочно). Классический цикл:
➕ Память не забивается (читается построчно).
➖ Медленно при миллионах строк (много системных вызовов read).
Для ускорения можно читать чанками (например, по 1 МБ):
➕ Гораздо меньше обращений к диску.
➕ Можно параллелить обработку чанков.
➖ Нужно дополнительно резать блоки на строки (grep, awk, cut).
▪️ Подход 2: mapfile / readarray. Современный и удобный способ загрузить файл в массив:
➕ Удобно: сразу доступ по индексам, можно обрабатывать батчами.
➕ Быстро для средних файлов (< 100–200 МБ).
➖ Огромные файлы (> 1 ГБ) забьют память.
Можно ограничить число строк:
А для потоковой обработки чанками:
➕ Работает как буферизация: читаем батчами по N строк.
➕ Ускоряет обработку за счёт снижения количества вызовов read.
В итоге получаем, что:
Для огромных логов (>1 ГБ) → читайте чанками (dd) или батчами (mapfile -n).
Для средних файлов → mapfile дает лучший баланс.
Для онлайн-потоков (tail -f) → старый добрый while read.
BashTex📱 #bash
Когда работаешь с большими логами (гигабайты), вопрос «как читать быстрее и без лишнего расхода памяти?» становится критичным. Сегодня рассмотрим два подхода:
while IFS= read -r line; do
# обработка строки
done < big.log
Для ускорения можно читать чанками (например, по 1 МБ):
while chunk=$(dd bs=1M count=1 status=none); do
# обработка блока текста (можно разбить на строки)
echo "$chunk" | grep "ERROR"
done < big.log
mapfile -t lines < big.log
echo "Первая строка: ${lines[0]}"
Можно ограничить число строк:
mapfile -t -n 1000 lines < big.log # только первые 1000 строк
А для потоковой обработки чанками:
exec 3< big.log
while mapfile -t -n 1000 batch <&3 && ((${#batch[@]})); do
printf '%s\n' "${batch[@]}" | grep "ERROR"
done
exec 3<&-
В итоге получаем, что:
Для огромных логов (>1 ГБ) → читайте чанками (dd) или батчами (mapfile -n).
Для средних файлов → mapfile дает лучший баланс.
Для онлайн-потоков (tail -f) → старый добрый while read.
BashTex
Please open Telegram to view this post
VIEW IN TELEGRAM
👍9
systemd timers: сценарии, которые cron не умеет
Часто cron хватает для простого раз в день, но когда расписание становится хитрым - systemd timers выигрывают. Покажу несколько кейсов, которые на cron делать больно, а здесь - просто.
1️⃣ Только в рабочие дни, с 9:00 до 18:00. Хотим, чтобы скрипт мониторинга нагрузки запускался каждые 15 минут в будни, но не ночью и не в выходные:
Mon..Fri - только будни;
09..18/15:00 - каждые 15 минут с 9:00 до 18:00.
Аналог на cron выглядел бы как несколько строк с костылями.
2️⃣ Persistent=true - выполнение пропущенных задач. Задача должна выполняться раз в день, даже если сервер был выключен ночью.
Если сервак в оффлайне, при старте systemd увидит пропущенное выполнение и запустит задачу.
У cron такого поведения нет - выключил машину, задача пропала.
3️⃣ Замороченные расписания - раз в час, но не в обед. Допустим, хотим запускать скрипт синхронизации каждые 60 минут, но исключить обеденный перерыв (с 12 до 13):
Здесь мы задали диапазоны часов с дыркой.
4️⃣ Несколько расписаний для одной задачи. Хочется иметь и ночной запуск (2:00), и дополнительный в пятницу вечером:
В одном таймере можно указать несколько OnCalendar.
5️⃣ Контроль за пропусками и сбоями. Если критичный скрипт не отработал, мы хотим это видеть. Добавим OnFailure:
Если бэкап упадет, то сразу вызовется alert.service (например, отправка сообщения в телегу).
BashTex📱 #bash #utils
Часто cron хватает для простого раз в день, но когда расписание становится хитрым - systemd timers выигрывают. Покажу несколько кейсов, которые на cron делать больно, а здесь - просто.
/etc/systemd/system/load-check.timer
[Timer]
OnCalendar=Mon..Fri *-*-* 09..18/15:00
Mon..Fri - только будни;
09..18/15:00 - каждые 15 минут с 9:00 до 18:00.
Аналог на cron выглядел бы как несколько строк с костылями.
[Timer]
OnCalendar=daily
Persistent=true
Если сервак в оффлайне, при старте systemd увидит пропущенное выполнение и запустит задачу.
У cron такого поведения нет - выключил машину, задача пропала.
[Timer]
OnCalendar=Mon..Fri *-*-* 09..11:00,13..18:00
Здесь мы задали диапазоны часов с дыркой.
[Timer]
OnCalendar=*-*-* 02:00:00
OnCalendar=Fri *-*-* 19:00:00
В одном таймере можно указать несколько OnCalendar.
/etc/systemd/system/backup.service
[Unit]
Denoscription=Nightly backup job
OnFailure=alert.service
Если бэкап упадет, то сразу вызовется alert.service (например, отправка сообщения в телегу).
BashTex
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥13👍7