uk
Feedback
BashTex | Linux

BashTex | Linux

Відкрити в Telegram

Авторский канал для тех, кто хочет глубже погрузиться в мир Linux. Подойдет для разработчиков, системных администраторов и DevOps Реклама: @dad_admin

Показати більше
2 524
Підписники
Немає даних24 години
-67 днів
+230 день
Архів дописів
Изоляция временных директорий для скриптов Каждый серьезный скрипт должен оставлять систему чистой после работы. Для этого нужно уметь создавать, использовать и безопасно очищать временные директории, особенно если в них хранятся промежуточные файлы, ключи или результаты сборки. Многие пишут так:

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

Параллельная обработка файлов без 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

Еще и с виртуалки.. BashTex 📱 #юмор
Еще и с виртуалки.. BashTex 📱 #юмор

Открытая конференция GetNet уже начинается — подключайтесь онлайн. Конфа для сетевиков и сисадминов - говорим о сетях, делимс
Открытая конференция GetNet уже начинается — подключайтесь онлайн. Конфа для сетевиков и сисадминов - говорим о сетях, делимся опытом, разбираем кейсы и отвечаем на вопросы Что будет: - Особенности дизайна Wi-Fi сетей для разных сценариев - Планируем Wi-Fi. Руководство "Как обмануть заказчика" - Wi-Fi. Вопросы безопасности. Защита периметра - Как построить МОЩНЫЙ прод и что этому может помешать? и др. Подключайся. Начинаем в 11-00

Захват переменных окружения из другой сессии Иногда нужно подцепить окружение работающего процесса: например, чтобы повторить его контекст, восстановить переменные или понять, с какими параметрами запущен сервис. Обычно это делают через 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

Сохраняем и восстанавливаем окружение Со временем окружение обрастает алиасами, функциями, переменными, путями - целой экосистемой, которую легко потерять при переключении между проектами. Что если сделать снимок текущего состояния 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

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

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

Системные уведомления через 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

Сравнение конфигураций между серверами Иногда нужно понять, чем конфиги на dev и prod отличаются, но без полного копирования или внешних тулз.
Задача: Сравнить /etc/nginx/ и /etc/systemd/ между двумя серверами, чтобы увидеть, какие файлы изменены, добавлены или удалены.
🛠 Пример скрипта:

#!/usr/bin/env bash

REMOTE="user@prod.example.com"
DIRS=("/etc/nginx" "/etc/systemd")
TMPDIR="/tmp/config-compare"
LOG="/tmp/config-diff.log"

mkdir -p "$TMPDIR"
> "$LOG"

for dir in "${DIRS[@]}"; do
  echo "Checking $dir ..." | tee -a "$LOG"

  rsync -avz --dry-run --delete "$REMOTE:$dir/" "$dir/" \
    | grep -E '^deleting|^>f' \
    | sed "s|^|$dir/ |" \
    >> "$TMPDIR/rsync.diff"
done

if [[ -s "$TMPDIR/rsync.diff" ]]; then
  echo -e "\nDifferences found:\n" | tee -a "$LOG"
  cat "$TMPDIR/rsync.diff" | tee -a "$LOG"
else
  echo "Configurations match across all directories." | tee -a "$LOG"
fi
▪️ Как это работает rsync --dry-run --delete - имитирует синхронизацию, но ничего не меняет, выводит список различий между каталогами. grep -E '^>f|^deleting' - фильтрует только изменения (новые или удалённые файлы). ▪️ Расширение скрипта при необходимости: 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

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

#!/usr/bin/env bash

SANDBOX="/opt/sandbox"
DEBIAN_MIRROR="http://deb.debian.org/debian"
ARCH=$(dpkg --print-architecture)

# Проверка зависимостей
for cmd in debootstrap chroot mount umount; do
  command -v $cmd >/dev/null || { echo "$cmd not found"; exit 1; }
done

# Создание окружения, если нет
if [[ ! -d "$SANDBOX" ]]; then
  echo "Создаю минимальную систему Debian..."
  sudo debootstrap --arch="$ARCH" stable "$SANDBOX" "$DEBIAN_MIRROR"
fi

# Монтируем системные точки
sudo mount -t proc /proc "$SANDBOX/proc"
sudo mount --rbind /sys "$SANDBOX/sys"
sudo mount --rbind /dev "$SANDBOX/dev"

# Добавим базовые бинари для экспериментов
sudo cp /bin/bash "$SANDBOX/bin/"
sudo cp /usr/bin/ls "$SANDBOX/usr/bin/"

echo "Входим в песочницу!"
sudo chroot "$SANDBOX" /bin/bash

# После выхода - очистка
echo "Отмонтирую ресурсы..."
sudo umount -l "$SANDBOX/proc" "$SANDBOX/sys" "$SANDBOX/dev"
Здесь: debootstrap ставит минимальный Debian прямо в каталог (/opt/sandbox) mount подключает системные псевдофайловые системы (/proc, /sys, /dev) chroot запускает bash внутри нового корня После выхода - все отмонтируется, и можно просто удалить /opt/sandbox
BashTex 📱 #bash #utils

Мужчины мечтают только об одном… BashTex 📱 #юмор

Генерация таблиц прогресса Реализация живой таблицы прогресса выполнения сприпта, обновляемой прямо в терминале - без внешних тулз, только 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

Автоматическое масштабирование ресурсов контейнеров Казалось бы, нетривиальная задача - нужно, чтобы контейнер автоматически подстраивался под нагрузку: добавлял 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

Продуманно BashTex 📱 #юмор
Продуманно BashTex 📱 #юмор

Меньше 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

Условное выполнение: && 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

Кто тормозит твой скрипт Когда скрипт вырастает, то понять, где он тратит время, становится сложно. Можно, конечно, запускать time ./script.sh, но это покажет лишь общую длительность. А если внутри десятки функций, то какая из них проседает? Решается просто: встроенным профилированием на чистом bash. ▪️ Идея Оборачиваем каждую функцию в таймер, считаем суммарное время вызовов и количество запусков. В конце - печатаем красивые итоги. 🛠 Минимальный пример

#!/usr/bin/env bash
declare -A FN_CALLS FN_TIME

# Обертка для функции
trace() {
    local fn=$1
    eval "
    $fn() {
        local start=\$(date +%s.%N)
        __${fn}_impl \"\$@\"
        local end=\$(date +%s.%N)
        local delta=\$(awk -v s=\$start -v e=\$end 'BEGIN{print e-s}')
        ((FN_CALLS[$fn]++))
        FN_TIME[$fn]=\$(awk -v a=\${FN_TIME[$fn]:-0} -v d=\$delta 'BEGIN{print a+d}')
    }
    "
}

# Пример функций
__work_impl() { sleep 0.2; }
__fast_impl() { echo ok >/dev/null; }

trace work
trace fast

# Нагрузочный сценарий
for i in {1..5}; do
  work
  fast
done

# Вывод статистики
echo -e "\nFunction Stats:"
for fn in "${!FN_CALLS[@]}"; do
    avg=$(awk -v t=${FN_TIME[$fn]} -v c=${FN_CALLS[$fn]} 'BEGIN{printf "%.4f", t/c}')
    printf "%-10s calls=%-3s total=%.4fs avg=%.4fs\n" "$fn" "${FN_CALLS[$fn]}" "${FN_TIME[$fn]}" "$avg"
done
▪️ Пример вывода:

Function Stats:
work       calls=5   total=1.0012s avg=0.2002s
fast       calls=5   total=0.0003s avg=0.0001s
▪️ Как это работает Каждая реальная функция хранится как __name_impl. Обернутый вариант считает время (date +%s.%N) до и после вызова. Сохраняет статистику в ассоциативных массивах. BashTex 📱 #bash

Оптимизация скриптов: меньше внешних команд - выше скорость Одна из самых недооцененных идей - это избегать лишних вызовов внешних утилит. Каждый grep, sed, awk, cut, wc - это новый процесс. Даже если он работает миллисекунды, то в большом цикле таких вызовов может быть сотни, и производительность падает кратно. ▪️ Примеры оптимизации ➖ Было:

count=$(grep "error" logfile | wc -l)
➕ Стало:

count=$(awk '/error/{c++} END{print c}' logfile)
или вообще без внешних утилит:

count=0
while IFS= read -r line; do
  [[ $line == *error* ]] && ((count++))
done < logfile
➖ Было:

name=$(echo "$filename" | cut -d. -f1)
➕ Стало:

name=${filename%%.*}
расширение строк - это встроенная возможность bash, без cut ➖ Было:

for f in $(ls *.log); do
  grep "fatal" "$f" >/dev/null && echo "$f"
done
➕ Стало:

for f in *.log; do
  grep -q "fatal" "$f" && echo "$f"
done
не вызываем ls, здесь glob уже делает свою работу ▪️ Фичи встроенного bash
${var,,} и ${var^^} - lowercase / uppercase без tr ${var//foo/bar} - замена без sed ${#array[@]} - длина массива без wc [[ $var == pattern* ]] - шаблоны без grep ((expr)) - арифметика без expr или bc mapfile -t lines < file - чтение файлов без cat
BashTex 📱 #bash

Следующая станция Ubuntu 25.10 BashTex 📱 #юмор
Следующая станция Ubuntu 25.10 BashTex 📱 #юмор

Границы роли DevOps всё больше размываются — пора разобраться, что действительно важно сегодня. 🎙️ 11 ноября в 18:00 мск при
Границы роли DevOps всё больше размываются — пора разобраться, что действительно важно сегодня. 🎙️ 11 ноября в 18:00 мск приглашаем на открытый разговор с практиками индустрии. Говорим честно — без теорий и готовых ответов. Что обсудим: ⚫️ Почему DevOps до сих пор значит разное для разных компаний ⚫️ Как читать между строк в вакансиях и отделять реальные требования от хотелок ⚫️ Карьерные пути от Junior до Lead — от операционных задач до стратегического влияния ⚫️ Стратегии роста, когда нужно знать всё и сразу Формат — круглый стол: живое обсуждение, реальные кейсы и ответы на ваши вопросы. ➡️ Получить ссылку на встречу — в боте

BashTex | Linux - Статистика та аналітика Telegram каналу @bash_tex