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

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

Реклама: @dad_admin
Download Telegram
Условное выполнение: && vs ; vs || vs !

У bash есть несколько операторов, которые управляют выполнением команд в зависимости от их кода возврата (0 - успех, ≠0 - ошибка). Разница кажется простой, но на практике часто приводит к тому, что люди не совсем понимают, что они используют.

▪️ ; - просто последовательность. Команды выполняются всегда, независимо от успеха/ошибки.


echo "start"; false; echo "end"
# => выведет start, потом end, даже если false упала


Используется, когда важно выполнить все подряд.

▪️ && - только при успехе. Выполнится вторая команда, если первая завершилась успешно (exit code 0).


mkdir data && echo "Папка создана"


Если mkdir упадет (например, папка уже есть), то echo не выполнится.

Полезно для цепочек успеха:


make build && make test && make deploy


▪️ || - только при ошибке. Выполнится, если первая команда завершилась с ошибкой.


ping -c1 host || echo "Хост недоступен"


Удобно для fallback-сценариев.

▪️ Комбинация && … || … Классический паттерн if-else в одну строку:


command && echo "OK" || echo "FAIL"


Подводный камень: если echo "OK" упадет, то выполнится и "FAIL".
Безопасный вариант - скобки:


command && { echo "OK"; } || { echo "FAIL"; }


▪️ ! - инверсия результата. Меняет код возврата команды на противоположный.


! true # вернет 1
! false # вернет 0


Часто встречается в условиях:


if ! grep -q "needle" file.txt; then
echo "Нет совпадений"
fi


Результаты:

; → всегда выполнить
&& → выполнить при успехе
|| → выполнить при ошибке
! → инвертировать результат
&& … || … → if-else, но лучше со скобками для надежности


BashTex 📱 #bash #utils
Please open Telegram to view this post
VIEW IN TELEGRAM
👍16
Меньше if и больше логики в одну строку

Когда вы пишете:


if command; then
echo "OK"
else
echo "Fail"
fi


В мире плачет один котенок…🥺 Ведь есть куда более лаконичная альтернатива:


command && echo "OK" || echo "Fail"


Это логические цепочки (&& и ||), которые позволяют писать коротко и эффективно.

▪️ Принцип short-circuit логики

Bash, как и многие языки, останавливает выполнение цепочки, если уже ясно, что дальше не нужно:

cmd1 && cmd2 - cmd2 выполняется только если cmd1 завершилась успешно (exit 0).
cmd1 || cmd2 - cmd2 выполняется только если cmd1 завершилась с ошибкой (exit ≠ 0).

▪️ Примеры таких условий

📍 Минимизация if:


mkdir newdir && cd newdir


создаст и сразу перейдёт, только если каталог успешно создан.

📍 Команда с fallback:


cp file.txt backup/ || echo "Не удалось скопировать!"


Если cp не удался - сработает echo.

📍 Успех или откат:


make build && echo "Успешно" || echo "Ошибка"


Если echo "Успешно" завершится с ошибкой (что редко, но возможно), то сработает и ||.
Чтобы избежать этого, оберните в скобки:


make build && { echo "Успешно"; } || { echo "Ошибка"; }


📍 Цепочка нескольких условий:


[ -d logs ] && cd logs && ls || echo "Нет каталога logs"


Проверяем наличие каталога
Если есть - заходим и выводим содержимое
Если нет - сообщение

📍 Проверка переменной:


[[ -n $API_KEY ]] && echo "Ключ найден" || echo "Нет ключа"


▪️Более сложные варианты использования

📍 Комбинирование условий:


[[ -f $file && -s $file ]] && echo "Файл существует и не пуст"


📍 Логическая цепочка до первой ошибки:


do_step1 && do_step2 && do_step3 || echo "Ошибка на одном из этапов"


📍 Псевдо-транзакции:


backup && update && restart || rollback


Если что-то пошло не так - rollback.

BashTex 📱 #bash #utils
Please open Telegram to view this post
VIEW IN TELEGRAM
👍14
Продуманно

BashTex 📱 #юмор
Please open Telegram to view this post
VIEW IN TELEGRAM
😁22👨‍💻1
Автоматическое масштабирование ресурсов контейнеров

Казалось бы, нетривиальная задача - нужно, чтобы контейнер автоматически подстраивался под нагрузку: добавлял CPU/memory, когда процесс задыхается, и освобождал ресурсы при простое. Все это без kubernetes, без swarm, просто bash + docker API. Попробуем реализовать.

▪️ План скрипта:

Скрипт опрашивает docker stats --no-stream
Сравнивает текущую загрузку CPU/памяти с порогами
При превышении - обновляет лимиты контейнера через docker update
При простое - возвращает лимиты назад
Все это циклически, с логированием и защитой от дребезга (частых переключений)


🛠 Пример скрипта (называться пусть будет auto-scale.sh)


#!/usr/bin/env bash

CONTAINER="webapp"
CPU_MAX=200000 # 200% CPU
CPU_MIN=50000 # 50% CPU
MEM_MAX="1g"
MEM_MIN="256m"

LOGFILE="/var/log/docker_autoscale.log"
INTERVAL=30
STABILITY=3 # сколько циклов подряд должна держаться нагрузка

cpu_high=0
cpu_low=0

log() { echo "$(date '+%F %T') $*" >> "$LOGFILE"; }

while true; do
read cpu mem <<<$(docker stats --no-stream --format "{{.CPUPerc}} {{.MemUsage}}" "$CONTAINER" \
| awk -F'[ %/]' '{printf "%d %d", $1, $2}')

if (( cpu > 80 )); then
((cpu_high++))
cpu_low=0
elif (( cpu < 30 )); then
((cpu_low++))
cpu_high=0
fi

if (( cpu_high >= STABILITY )); then
log "High load detected ($cpu%). Increasing limits..."
docker update --cpus="2.0" --memory="$MEM_MAX" "$CONTAINER" >/dev/null
cpu_high=0
elif (( cpu_low >= STABILITY )); then
log "Low load detected ($cpu%). Decreasing limits..."
docker update --cpus="0.5" --memory="$MEM_MIN" "$CONTAINER" >/dev/null
cpu_low=0
fi

sleep "$INTERVAL"
done


BashTex 📱 #bash
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥8
Генерация таблиц прогресса

Реализация живой таблицы прогресса выполнения сприпта, обновляемой прямо в терминале - без внешних тулз, только 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 📱 #bash #utils
Please open Telegram to view this post
VIEW IN TELEGRAM
👍4
This media is not supported in your browser
VIEW IN TELEGRAM
Мужчины мечтают только об одном…

BashTex 📱 #юмор
Please open Telegram to view this post
VIEW IN TELEGRAM
😁14🔥4🫡3
Создание песочницы для экспериментов

Иногда нужно проверить какую-то команду, поиграться с конфигами или собрать пакет - но без риска сломать систему. Для этого в linux можно сделать свою мини-песочницу, используя старое доброе chroot и немного bash.

chroot - это механизм, который меняет корень файловой системы для процесса. Все, что он видит, находится внутри изолированного каталога, и даже rm -rf / не затронет настоящую систему (если, конечно, настроено правильно 🤓).


🛠 Скрипт создающий песочницу:


#!/usr/bin/env bash

SANDBOX="/opt/sandbox"
DEBIAN_MIRROR="https://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 📱 #bash #utils
Please open Telegram to view this post
VIEW IN TELEGRAM
👍11
Сравнение конфигураций между серверами

Иногда нужно понять, чем конфиги на dev и prod отличаются, но без полного копирования или внешних тулз.

Задача:

Сравнить /etc/nginx/ и /etc/systemd/ между двумя серверами, чтобы увидеть, какие файлы изменены, добавлены или удалены.


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


#!/usr/bin/env bash

REMOTE="[email protected]"
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' - фильтрует только изменения (новые или удалённые файлы).

▪️ Расширение скрипта при необходимости:

1️⃣ Сравнение содержимого файлов:


ssh "$REMOTE" "cat /etc/nginx/nginx.conf" > /tmp/remote.conf
diff -u /etc/nginx/nginx.conf /tmp/remote.conf || echo "nginx.conf differs!"


2️⃣ Генерация markdown отчета:


echo -e "## Config diff report\n\`\`\`\n$(cat $TMPDIR/rsync.diff)\n\`\`\`" > /tmp/diff_report.md


3️⃣Уведомление в телегу при изменениях:


[[ -s $TMPDIR/rsync.diff ]] && curl -s -F "text=$(cat $TMPDIR/rsync.diff)" \
"https://api.telegram.org/bot$TOKEN/sendMessage?chat_id=$CHAT_ID"


BashTex 📱 #bash
Please open Telegram to view this post
VIEW IN TELEGRAM
👍8🔥2
Системные уведомления через D-Bus и bash

Bash умеет не только писать логи в консоль - он может отправлять системные уведомления прямо в графическую среду, используя D-Bus. Это хороший способ сообщить пользователю о результатах скрипта, ошибках или завершении задач, не залезая в UI.

1️⃣ Простой способ - notify-send. Самая быстрая интеграция:


notify-send "Бэкап завершен" "Все файлы успешно сохранены" --icon=dialog-information


Работает через D-Bus (org.freedesktop.Notifications). Можно добавить приоритет, срок жизни, категории:


notify-send \
--urgency=critical \
--expire-time=10000 \
--app-name="BackupScript" \
"Ошибка резервного копирования" \
"Недостаточно места на диске!"


2️⃣ Напрямую через dbus-send. Если notify-send недоступен (например, в минимальной среде),
можно напрямую вызвать метод 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, минуя внешние обертки. Можно внедрить в сценарии, где нужно абсолютное управление уведомлениями.

3️⃣ Уведомления в фоновом режиме. Если скрипт работает из cron или systemd-сервиса, нужно указать сеансовую шину D-Bus пользователя. Например, так:


export DBUS_SESSION_BUS_ADDRESS="unix:path=/run/user/$(id -u)/bus"
notify-send "Backup completed" "Проверено $(date)"


Это позволяет отправлять уведомления от root в сессию конкретного пользователя.

4️⃣ Практический пример


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


Каждое выполнение скрипта будет сопровождаться визуальными уведомлениями в системе.

▪️ Динамические уведомления. Можно обновлять уведомление (пример для GNOME/KDE, не все среды поддерживают):


ID=$(notify-send "Выполняется резервное копирование..." --print-id)
sleep 5
notify-send "Завершено" "Файлы успешно сохранены" --replace-id="$ID"


BashTex 📱 #bash #utils
Please open Telegram to view this post
VIEW IN TELEGRAM
👍7🔥2🤨1
Вставка последнего аргумента без копипасты

Сколько раз вы выполняли команду, вроде:


cp file.txt /tmp/somedir/


а потом через секунду нужно:


cd /tmp/somedir/


и снова набираете руками путь или выделяете путь?
Есть способ не повторять последнее слово - bash это помнит.

1️⃣ Alt + . - вставка последнего аргумента

В любой момент нажмите Alt + . - и bash подставит последний аргумент предыдущей команды.


cp file.txt /tmp/somedir/
cd <Alt+.>


превратится в


cd /tmp/somedir/


Нажимайте Alt + . несколько раз, чтобы пройтись по аргументам из истории (Bash перебирает их назад).

2️⃣ Альтернатива: !$ и !. Тоже самое, но в виде подстановки из истории:


cd !$


или


cd !.


!$ - последний аргумент предыдущей команды.
!. - то же самое, но безопаснее (некоторые шеллы по-разному интерпретируют $).

3️⃣ Примеры

Быстрое удаление того, что только что создали:


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 📱 #bash #utils
Please open Telegram to view this post
VIEW IN TELEGRAM
👍14🔥2😁1
Сохраняем и восстанавливаем окружение

Со временем окружение обрастает алиасами, функциями, переменными, путями - целой экосистемой, которую легко потерять при переключении между проектами. Что если сделать снимок текущего состояния shell и потом восстановить его одной командой?

1️⃣ Сохранение окружения. Bash позволяет получить всю информацию о текущем состоянии:


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, и т.д.

2️⃣ Восстановление окружения. Чтобы применить снимок в другой сессии:


source ~/.bash_snapshot


Все ваши функции, алиасы и переменные возвращаются как были.

3️⃣ Пример автоматизации. Сделаем удобный скрипт:


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


4️⃣ Сравнение. Финалочка, можно сравнить два снимка:


diff <(grep -v '^#' dev.env) <(grep -v '^#' prod.env)


покажет, что отличается между dev и prod.

BashTex 📱 #bash #utils
Please open Telegram to view this post
VIEW IN TELEGRAM
👍9
Захват переменных окружения из другой сессии

Иногда нужно подцепить окружение работающего процесса: например, чтобы повторить его контекст, восстановить переменные или понять, с какими параметрами запущен сервис.

Обычно это делают через export или env, но можно достать переменные прямо из /proc, без доступа к shell-сессии.

▪️ Чтение окружения процесса. Каждый процесс в Linux хранит свои переменные в:


/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


▪️ Захват и использование в текущей сессии. Если нужно подгрузить это окружение в текущий bash:


PID=1234
eval "$(tr '\0' '\n' < /proc/$PID/environ | sed 's/^/export /')"


Теперь ты в том же окружении, что и процесс $PID.

Будет полезно, если:

хочешь повторить окружение демона (nginx, systemd, custom app);
нужно пересоздать контекст для отладки;
запускаешь скрипт от того же пользователя, что и процесс.


▪️ Извлечение конкретной переменной. Например, достанем PATH:


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


▪️ Захват окружения systemd-сервисов. Systemd не всегда передает окружение дальше, но его можно вытащить по PID активного процесса:


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) будет запускаться с теми же переменными, что и процесс веб-сервера.

🌟 Доступно только для процессов, владельцем которых ты являешься (или root).

BashTex 📱 #bash #utils
Please open Telegram to view this post
VIEW IN TELEGRAM
👍7
Еще и с виртуалки..

BashTex 📱 #юмор
Please open Telegram to view this post
VIEW IN TELEGRAM
😁21👍3
Параллельная обработка файлов без GNU Parallel

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


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 задачи одновременно


▪️ Ещё лаконичнее через xargs


ls *.log | xargs -n1 -P4 bash -c 'gzip "$0"'


-n1 - по одному аргументу на процесс
-P4 - максимум 4 параллельных процесса
bash -c 'gzip "$0"' - шаблон выполнения


🌟 Работает даже в старых системах (без wait -n).

▪️ Чуть сложнее: обработка с логированием и статусом


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 📱 #bash #utils
Please open Telegram to view this post
VIEW IN TELEGRAM
👍7
Изоляция временных директорий для скриптов

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

Многие пишут так:


TMPDIR="/tmp/myscript"
mkdir -p "$TMPDIR"
# ...
rm -rf "$TMPDIR"


Это рискованно, потому что:

- Возможна гонка при mkdir в /tmp
- Если скрипт завершится с ошибкой - rm -rf не выполнится
- Несколько процессов перетрут файлы друг друга

▪️ Решение: mktemp + trap


TMPDIR=$(mktemp -d -t myscript.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 myscript.XXXXXX)
chmod 700 "$TMPDIR"


Полезно, если скрипт работает под рутом и обрабатывает конфиденциальные данные.

BashTex 📱 #bash #utils
Please open Telegram to view this post
VIEW IN TELEGRAM
👍8
Динамическое создание и использование временных файлов

Иногда хочется или нужно передавать данные между процессами без создания реальных временных файлов: быстро, безопасно и с контролем потока. Вот тут в дело вступают именованные каналы (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
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥8
Пс, парень, давай поговорим о лучших дистрибутивах

BashTex 📱 #юмор
Please open Telegram to view this post
VIEW IN TELEGRAM
😁11👨‍💻1
Собственная корзина

С помощью 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 #utils
Please open Telegram to view this post
VIEW IN TELEGRAM
👍7🔥2
Реализация кэша команд

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

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

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

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

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


# ~/.bash_cache.sh

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

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

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

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


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


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


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


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


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


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


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

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


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


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


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


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

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

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


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

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

#!/usr/bin/env bash

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

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

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

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

exec 3>&-


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

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

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

#!/usr/bin/env bash

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

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

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

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

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


▪️ HTTPS?

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

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


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

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

BashTex 📱 #юмор
Please open Telegram to view this post
VIEW IN TELEGRAM
😁15👍4