10 функций, которые помогут сделать bash-скрипты чуть более элегантными

1. log(): простой логгер с метками времени

log() {
  local level="$1"; shift
  local timestamp
  timestamp=$(date "+%Y-%m-%d %H:%M:%S")
  echo "[$timestamp] [$level] $*" >&2
}

Эта функция выводит все логи в одном формате — с меткой времени и уровнем логирования (INFO, ERROR и т.д.). Очень удобно и для отладки, и для продакшн-логов.

Пример:

log "INFO" "Deployment started"
log "ERROR" "Failed to connect to the database"

2. retry(): повтор команды с экспоненциальной задержкой

retry() {
  local retries="$1"; shift
  local count=0 exit_code=0
  until "$@"; do
    exit_code=$?
    ((count++))
    if ((count >= retries)); then
   log "ERROR" "Command failed after $retries attempts: $*"
      return "$exit_code"
    fi
    log "WARN" "Retrying ($count/$retries)..."
    sleep $((count * 2))
  done
}

Незаменима при работе с ненадёжными сервисами (например, HTTP API), которые иногда могут отвалиться. С этой функцией ваш скрипт станет гораздо устойчивее к таким сбоям.

Пример:

retry 5 curl -sf http://localhost:8080/healthz

3. check_required_env(): проверка обязательных переменных окружения

check_required_env() {
  for var in "$@"; do
    if [[ -z "${!var:-}" |]]; then
   log "ERROR" "Missing required environment variable: $var"
      exit 1
    fi
  done
}

Позволяет сразу же завершить выполнение скрипта, если чего-то важного не хватает — например, секретного ключа или конфигурационного параметра.

Пример:

check_required_env AWS_ACCESS_KEY_ID AWS_SECRET_ACCESS_KEY

4. load_config(): загрузка .env-файлов или конфигов в формате ключ-значение

load_config() {
  local config_file="$1"
  [[ -f "$config_file" |]] || { log "ERROR" "Config file not found: $config_file"; return 1; }
 
  while IFS='=' read -r key value || [[ -n "$key" |]]; do
    [[ "$key" =~ ^\s*# |]] || [[ -z "$key" |]] && continue
    key=$(echo "$key" | xargs)
    value=$(echo "$value" | sed 's/^"\(.*\)"$/\1/' | xargs)
    export "$key=$value"
  done < "$config_file"
}

Позволяет выносить конфигурацию за пределы скрипта — по канонам 12-factor приложений. Можно грузить .env или любой файл с переменными в формате KEY=VALUE.

Пример:

load_config "./myapp.env"

5. safe_rm(): перемещает в корзину вместо удаления

safe_rm() {
  local file="$1"
  [[ -e "$file" |]] || { log "WARN" "File not found: $file"; return 1; }
 
  local trash_dir="$HOME/.trash"
  mkdir -p "$trash_dir"
  local base
  base=$(basename "$file")
  local timestamp
  timestamp=$(date +%s)
  mv "$file" "$trash_dir/${base}_${timestamp}_$$" && \
    log "INFO" "Moved $file to trash instead of deleting"
}

Полезно в ситуациях, когда скрипт делает что-то потенциально разрушительное. Вместо удаления — просто перемещает файл в «корзину».

Пример:

safe_rm "config.yml"

6. confirm(): запрос подтверждения (Yes/No)

confirm() {
  local prompt="${1:-Are you sure?} (y/n): "
  while true; do
    read -rn 1 -p "$prompt" response
    echo
    case "${response,,}" in
   y) return 0 ;;
      n) return 1 ;;
      *) echo "Please enter y or n." ;;
    esac
  done
}

Добавляет защиту от случайного запуска опасных операций. Простое «уверены ли вы?» перед тем, как что-то пойдёт не так.

Пример:

if confirm "Are you sure you want to delete production DB?"; then
  delete_database
fi

7. monitor_usage(): быстрая проверка CPU и памяти

monitor_usage() {
  echo "CPU: $(top -bn1 | grep 'Cpu(s)' | awk '{print $2 + $4}')%"
  echo "Memory: $(free -m | awk '/Mem:/ { printf("%.2f%%", $3/$2 * 100.0) }')"
}

Простой способ быстро проверить состояние системы — особенно полезно перед деплоем или сразу после него.

Лучше всего работает на Linux. На macOS может не сработать без установки аналогов top и free.

8. require_command(): проверка, что нужные утилиты установлены

require_command() {
  for cmd in "$@"; do
    if ! command -v "$cmd" &>/dev/null; then
   log "ERROR" "Missing required command: $cmd"
      exit 1
    fi
  done
}

Скрипт сразу падает, если не хватает нужных CLI-инструментов — например, jq, curl или aws.

Пример:

require_command jq docker aws

9. wrap_with_timer(): измеряет время выполнения команды

wrap_with_timer() {
  local start end elapsed
  start=$(date +%s)
  "$@"
  local exit_code=$?
  end=$(date +%s)
  elapsed=$((end - start))
  log "INFO" "Command '$*' completed in ${elapsed}s"
  return $exit_code
}

Помогает отслеживать производительность и находить узкие места.

Пример:

wrap_with_timer run_long_script

10. cleanup(): удаляет временные файлы при выходе

cleanup() {
  log "INFO" "Cleaning up..."
  rm -f /tmp/my-temp-file
}
trap cleanup EXIT

Чтобы скрипт не оставлял за собой мусор (например, временные файлы или lock-файлы), даже если завершился с ошибкой.

Бонус: шаблон скрипта с использованием всех этих функций

#!/usr/bin/env bash
set -euo pipefail
 
# Import shared functions
source ./functions.sh
 
# Ensure required commands and environment
require_command jq curl docker
check_required_env ENVIRONMENT
 
# Load environment-specific configuration
load_config "./.env.${ENVIRONMENT}"
 
# Begin script
log "INFO" "Starting automation for $ENVIRONMENT"
 
wrap_with_timer deploy_application