BashTex | Linux – Telegram
BashTex | Linux
2.5K subscribers
47 photos
9 videos
288 links
Авторский канал для тех, кто хочет глубже погрузиться в мир Linux.

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

Реклама: @dad_admin
Download Telegram
Please open Telegram to view this post
VIEW IN TELEGRAM
😁20🔥1
Авторазрешение зависимостей скрипта через dynamic sourcing

В больших bash-проектах часто есть несколько вспомогательных файлов (.sh или .bash), которые содержат функции и настройки. Проблема в том, что:

📍Скрипт может запуститься в другом каталоге - пути сломаются
📍Некоторые модули могут отсутствовать или лежать в нестандартных местах
📍При изменении структуры проекта придётся вручную переписывать source

Решение: динамическое подключение модулей

1️⃣ Автоматический source по списку зависимостей


#!/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


Такой подход позволяет легко добавлять новые пути в поиск - модуль подтянется сам.

2️⃣ Dynamic sourcing по зависимостям из конфигурации. Можно вынести список зависимостей в отдельный файл deps.txt:


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


3️⃣ Поиск относительно текущего скрипта. Чтобы скрипт работал из любого места:


SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
source "$SCRIPT_DIR/lib/logger.sh"


▪️ Плюсы подхода:

Нет жестких путей;
Можно запускать в разных окружениях;
Удобно для open-source проектов и shared-библиотек;
Легко расширять - просто добавь файл в deps.txt.

BashTex 📱 #bash
Please open Telegram to view this post
VIEW IN TELEGRAM
👍4
Автонастройка нового сервера по шаблону

Один скрипт - и свежий VPS готов к работе.

Скрипт будет:

📍Создать нового пользователя с sudo;
📍Настраивать SSH-ключи и запрещать парольный вход;
📍Отключать root-доступ;
📍Настраивать базовый firewall (ufw);
📍Обновлять пакеты

🛠 Сам скрипт:


#!/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 📱 #bash
Please open Telegram to view this post
VIEW IN TELEGRAM
👍10
Когда я установлю Linux, я никому не скажу, но знаки будут

BashTex 📱 #юмор
Please open Telegram to view this post
VIEW IN TELEGRAM
😁19
Однострочники: меньше кода - быстрее результат

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

1️⃣ Быстрый поиск файла по имени


find / -type f -name "*.log" 2>/dev/null


Ищет .log файлы в системе, игнорируя ошибки доступа.

2️⃣ Топ-10 самых больших файлов


du -ah /path | sort -rh | head -n 10

Показывает, что занимает место.

3️⃣ Проверка доступности сайта


curl -Is https://bashtex.com | head -n1


Отдаёт HTTP-статус (200 OK, 404 Not Found и т.д.).

4️⃣ Массовое переименование файлов


for f in *.txt; do mv "$f" "${f%.txt}.bak"; done


Меняет расширение .txt на .bak у всех файлов в папке.

5️⃣ Замер времени выполнения команды


time tar -czf backup.tar.gz /data


Показывает, сколько заняла команда.

6️⃣ Быстрый бэкап с датой


tar -czf backup-$(date +%F).tar.gz /path/to/dir


Файл будет называться backup-2025-09-11.tar.gz.

7️⃣ Мониторинг нагрузки в реальном времени


watch -n 1 "df -h / && free -m"


Каждую секунду показывает дисковое и RAM-состояние.

8️⃣ Удаление файлов старше N дней


find /tmp -type f -mtime +7 -delete


Чистит /tmp от старых файлов.

9️⃣ Загрузка и распаковка архива


curl -L https://site.ru/file.tar.gz | tar -xz


Скачивает и сразу распаковывает.

1️⃣0️⃣ Поиск текста в файлах


grep -R "ERROR" /var/log/


Находит все упоминания ERROR в логах.

BashTex 📱 #bash
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥12
Однострочники: меньше кода - быстрее результат. Часть 2

Продолжение верхнего поста, но с более харкорными фишками.

1️⃣ Параллельное выполнение задач


seq 1 10 | xargs -n1 -P4 bash -c 'echo "Job $0"; sleep 2'


Выполняет 4 задачи одновременно, распределяя их по входным данным.

2️⃣ Автосоздание многоуровневых JSON из списка


awk -F= '{gsub(/ /,"",$1); printf "\"%s\":\"%s\",\n",$1,$2}' vars.txt | sed '$ s/,$//; 1s/^/{/; $s/$/}/'


Превращает key=value в JSON-структуру без jq.

3️⃣ Замена текста в пачке файлов с сохранением даты


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


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

4️⃣ Потоковая упаковка только изменённых файлов


find /src -type f -newermt "2025-09-12" -print0 | tar --null -T - -czf patch.tar.gz


Создает архив только с файлами новее указанной даты.

5️⃣ «Ловля» пакетов через tcpdump с фильтром и автопарсингом


tcpdump -l -i eth0 "tcp port 80" | stdbuf -oL awk '/Host:/{print $2}'


В реальном времени выводит посещаемые хосты.

6️⃣ Горячее редактирование конфигов на удаленке


ssh user@server "sed -i '/^#MaxAuthTries/s/^#//' /etc/ssh/sshd_config && systemctl restart sshd"


Меняет параметр в конфиге и перезапускает сервис без SCP.

7️⃣ Наблюдение за PID и его автокилл


while pidof myproc >/dev/null; do sleep 1; done && echo "Process died"


Ждет завершения процесса и реагирует.

8️⃣ Объединение нескольких логов в один с отметкой источника


tail -F app1.log | sed 's/^/[APP1] /' & \
tail -F app2.log | sed 's/^/[APP2] /' & wait


В реальном времени сливает логи с пометками.

9️⃣ Лайв-диагностика сети с графиком


ping -i 0.2 8.8.8.8 | awk -F'=' '/time=/{print $NF}' | gnuplot -p -e "plot '<cat' with lines"


Пинг с живой отрисовкой графика без промежуточных файлов.

1️⃣0️⃣ «Самообновляющийся» скрипт


curl -s https://bashtex.com/mynoscript.sh -o "$0" && exec bash "$0" "$@"


Обновляет себя и перезапускается.

BashTex 📱 #bash
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 - включено):


FLAG_READ=1 # 0001
FLAG_WRITE=2 # 0010
FLAG_EXEC=4 # 0100
FLAG_DELETE=8 # 1000


▪️ Установка флага (OR |)


rights=0
rights=$(( rights | FLAG_READ | FLAG_WRITE ))
echo $rights # 3 (0001 + 0010 = 0011)


▪️ Проверка флага (AND &)


if (( rights & FLAG_WRITE )); then
echo "Есть право записи"
fi


▪️ Сброс флага (AND + NOT &~)


rights=$(( rights & ~FLAG_READ ))
echo $rights # теперь без read


▪️ Инверсия (XOR ^)


rights=$(( rights ^ FLAG_EXEC )) # переключение: если был - уберется, если не был - включится


▪️ Практические кейсы
1️⃣ Система разрешений в скриптах. Можно задавать доступ к операциям через биты вместо кучи if:


if (( user_flags & FLAG_DELETE )); then
rm "$file"
fi


2️⃣ Упаковка нескольких состояний в одно число. Например, код статуса сервиса:


STATUS_RUNNING=1
STATUS_RESTARTING=2
STATUS_FAILED=4

status=$(( STATUS_RUNNING | STATUS_RESTARTING ))

(( status & STATUS_FAILED )) && echo "Ошибка!"


3️⃣ Быстрые флаги для CLI-опций


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]"


4️⃣ Флаги как компактная замена массивов. Вместо массива enabled_features=(...) можно хранить все в одной переменной. Это особенно ценно при передаче значений между процессами.

BashTex 📱 #bash #utils
Please open Telegram to view this post
VIEW IN TELEGRAM
👍10
Создание оффлайн инсталлятора из .deb зависимостей

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

▪️ Установка dpkg-repack. На машине с интернетом:


sudo apt install dpkg-repack


▪️ Репак пакета в .deb. Если пакет уже стоит в системе:


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


▪️ Итог - "папка-инсталлятор". В каталоге будут все нужные .deb:


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 📱 #bash #utils
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 (&) и ограничитель числа потоков.

🛠 Пример скрипта


#!/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


▪️ Пример hosts.txt


server1
192.168.1.12
192.168.1.15


▪️ Запуск


./parallel-ssh.sh "uptime"


Вывод будет помечен [host], чтобы не путались строки.

BashTex 📱 #bash
Please open Telegram to view this post
VIEW IN TELEGRAM
👍9
Автоматическое формирование changelog из git-истории

Любой проект со временем обрастает десятками коммитов. Но превращать историю 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)


▪️ Можно также добавить:

📍добавить дату релиза: --date=short --pretty=format:"- %s (%an, %ad)";
📍разделить по типам коммитов (feat, fix, docs) с помощью grep;
📍генерировать changelog только для новых коммитов с последнего релиза.

BashTex 📱 #bash
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥8
Скрипт для массового обновления SSH-ключей пользователей на кластере

Когда у вас десятки серверов и несколько пользователей, ручное обновление ~/.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"


▪️ Что делает скрипт

📍перебирает все хосты из hosts.txt;
📍для каждого пользователя заливает новый authorized_keys;
📍выставляет правильные права (700 для .ssh, 600 для ключа);
📍пишет лог успешных и неудачных обновлений.

BashTex 📱 #bash
Please open Telegram to view this post
VIEW IN TELEGRAM
👍11
Использование coproc для работы с асинхронными потоками

Многие знают про & и 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 живет, пока вы его не закроете. То есть можно посылать команды и читать ответы несколько раз.

▪️ Практические кейсы
📍 Асинхронные задачи с обратной связью. Можно запускать ping, tcpdump или tail -f в coproc, а потом считывать поток строк построчно:


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 можно называть:


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 📱 #bash #utils
Please open Telegram to view this post
VIEW IN TELEGRAM
👍7
Встраивание мини-HTTP-сервера на netcat + bash (для локальных API)

Иногда хочется быстро поднять легковесный 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}")'"}'


📍простой healthcheck для Docker: curl localhost:8080/health
📍мини-API для локальных скриптов (например, статус бэкапа).

Такой подход позволяет собрать сверхлегкий REST-like API прямо из bash - без сторонних веб-фреймворков.

BashTex 📱 #bash #utils
Please open Telegram to view this post
VIEW IN TELEGRAM
👍7🔥5
Открыл книгу, а закрыть не получается..

BashTex 📱 #юмор
Please open Telegram to view this post
VIEW IN TELEGRAM
😁38👍5🔥1🫡1
Автоматическое создание LVM-снапшотов с очисткой старых копий

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 📱 #bash
Please open Telegram to view this post
VIEW IN TELEGRAM
👍6
Продвинутые regex выражения и их подводные камни

В bash оператор [[ string =~ regex ]] дает инструмент для проверки строк по регулярным выражениям. Но у него есть особенности, о которых часто забывают.

▪️ Базовый синтаксис


str="user123"
if [[ $str =~ ^user[0-9]+$ ]]; then
echo "Совпало"
else
echo "Нет совпадения"
fi


=~ - оператор сравнения по регулярному выражению (ERE)
без кавычек вокруг regex (!), иначе он станет строкой

▪️ Подводные камни

1️⃣ Кавычки ломают regex


[[ $str =~ "^[0-9]+$" ]] # НЕ работает как regex
[[ $str =~ ^[0-9]+$ ]] # правильно


2️⃣ Пробелы в regex → нужно экранировать


[[ "foo bar" =~ foo\ bar ]] # правильно


3️⃣ Группы и BASH_REMATCH


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] — группы.

4️⃣ Regex всегда интерпретируется как ERE (Extended Regex)

+, ?, | работают без \
Но нет поддержки PCRE (\d, \w, lookahead и т.п.)

5️⃣ Неочевидные совпадения из-за пустого паттерна


regex="^$" # пустая строка
[[ "abc" =~ $regex ]] && echo "совпало" # неожиданно не совпадает


6️⃣ Вложенные переменные в regex. Если подставлять переменную в regex, лучше использовать () для контроля:


re="[0-9]{2}"
[[ "2025" =~ ($re) ]] && echo "нашли: ${BASH_REMATCH[1]}"


▪️ Полезные приемы

1️⃣ Проверка на число:


[[ $x =~ ^[0-9]+$ ]] && echo "число"



2️⃣ Проверка на IPv4:


[[ $ip =~ ^([0-9]{1,3}\.){3}[0-9]{1,3}$ ]] && echo "ipv4"


3️⃣ Проверка на slug (латиница, цифры, дефис):


[[ $slug =~ ^[a-z0-9-]+$ ]] || echo "некорректный slug"


BashTex 📱 #bash
Please open Telegram to view this post
VIEW IN TELEGRAM
👍7
Оптимизация чтения больших файлов

Когда работаешь с большими логами (гигабайты), вопрос «как читать быстрее и без лишнего расхода памяти?» становится критичным. Сегодня рассмотрим два подхода:

▪️ Подход 1: Чтение чанками (построчно или блочно). Классический цикл:


while IFS= read -r line; do
# обработка строки
done < big.log


Память не забивается (читается построчно).
Медленно при миллионах строк (много системных вызовов read).

Для ускорения можно читать чанками (например, по 1 МБ):


while chunk=$(dd bs=1M count=1 status=none); do
# обработка блока текста (можно разбить на строки)
echo "$chunk" | grep "ERROR"
done < big.log


Гораздо меньше обращений к диску.
Можно параллелить обработку чанков.
Нужно дополнительно резать блоки на строки (grep, awk, cut).

▪️ Подход 2: mapfile / readarray. Современный и удобный способ загрузить файл в массив:


mapfile -t lines < big.log
echo "Первая строка: ${lines[0]}"


Удобно: сразу доступ по индексам, можно обрабатывать батчами.
Быстро для средних файлов (< 100–200 МБ).
Огромные файлы (> 1 ГБ) забьют память.

Можно ограничить число строк:


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<&-


Работает как буферизация: читаем батчами по N строк.
Ускоряет обработку за счёт снижения количества вызовов read.

В итоге получаем, что:

Для огромных логов (>1 ГБ) → читайте чанками (dd) или батчами (mapfile -n).
Для средних файлов → mapfile дает лучший баланс.
Для онлайн-потоков (tail -f) → старый добрый while read.

BashTex 📱 #bash
Please open Telegram to view this post
VIEW IN TELEGRAM
👍9
systemd timers: сценарии, которые cron не умеет

Часто cron хватает для простого раз в день, но когда расписание становится хитрым - systemd timers выигрывают. Покажу несколько кейсов, которые на cron делать больно, а здесь - просто.

1️⃣ Только в рабочие дни, с 9:00 до 18:00. Хотим, чтобы скрипт мониторинга нагрузки запускался каждые 15 минут в будни, но не ночью и не в выходные: /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 выглядел бы как несколько строк с костылями.

2️⃣ Persistent=true - выполнение пропущенных задач. Задача должна выполняться раз в день, даже если сервер был выключен ночью.


[Timer]
OnCalendar=daily
Persistent=true


Если сервак в оффлайне, при старте systemd увидит пропущенное выполнение и запустит задачу.
У cron такого поведения нет - выключил машину, задача пропала.

3️⃣ Замороченные расписания - раз в час, но не в обед. Допустим, хотим запускать скрипт синхронизации каждые 60 минут, но исключить обеденный перерыв (с 12 до 13):


[Timer]
OnCalendar=Mon..Fri *-*-* 09..11:00,13..18:00


Здесь мы задали диапазоны часов с дыркой.

4️⃣ Несколько расписаний для одной задачи. Хочется иметь и ночной запуск (2:00), и дополнительный в пятницу вечером:


[Timer]
OnCalendar=*-*-* 02:00:00
OnCalendar=Fri *-*-* 19:00:00


В одном таймере можно указать несколько OnCalendar.

5️⃣ Контроль за пропусками и сбоями. Если критичный скрипт не отработал, мы хотим это видеть. Добавим OnFailure: /etc/systemd/system/backup.service


[Unit]
Denoscription=Nightly backup job
OnFailure=alert.service


Если бэкап упадет, то сразу вызовется alert.service (например, отправка сообщения в телегу).

BashTex 📱 #bash #utils
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥13👍7
Перехват stdout/stderr отдельных функций и подпрограмм

Обычно мы перенаправляем вывод глобально: myfunc >out.log 2>err.log. Но что, если нужно внутри скрипта гибко ловить stdout/stderr отдельных функций и даже подпрограмм, не ломая общий вывод? Тут могут помочь динамические файловые дескрипторы через exec {fd}>.

▪️ Пример 1. Логирование только stdout функции


logfile="stdout.log"

myfunc() {
echo "info: started"
echo "debug: internal"
echo "error: fail" >&2
}

exec {fd}> "$logfile" # создаем FD
myfunc 1>&$fd # stdout -> в файл, stderr остаётся на экран
exec {fd}>&- # закрываем FD


stdout (info, debug) уйдет в stdout.log;
stderr (error) появится в терминале.

▪️ Пример 2. Перехват stderr подпрограммы


errlog="stderr.log"

exec {fd}> "$errlog"
{ ls /root; echo "done"; } 2>&$fd
exec {fd}>&-


Ошибки от ls /root попадут в stderr.log, а «done» останется в терминале.

▪️ Пример 3. Отдельный канал для «отладки» внутри функции. Иногда хочется иметь третий тип вывода, помимо stdout/stderr.


exec {dbg}> debug.log # отдельный канал

mycalc() {
echo "42" # обычный результат
echo "step1 ok" >&$dbg
echo "step2 ok" >&$dbg
}

result=$(mycalc)
echo "result = $result"
exec {dbg}>&-


Так можно вести невидимый debug-лог параллельно с обычной работой.

▪️ Пример 4. Подмена stdout на время вызова


mycmd() { echo "normal out"; }

{
exec {fd}> redirected.log
mycmd >&$fd
exec {fd}>&-
}
echo "done"


Прием позволяет временно заменить stdout и потом вернуть его обратно.

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