Bash Cheatsheet

Скелет защищённого скрипта

#!/usr/bin/env bash
set -euo pipefail              # exit on error/undefined/pipe failure
IFS=$'\n\t'

readonly SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
readonly LOG="/tmp/$(basename "$0").log"

log() { echo "[$(date +%FT%T)] $*" | tee -a "$LOG"; }

cleanup() {
  log "cleanup..."
}
trap cleanup EXIT
trap 'log "Error on line $LINENO"' ERR

usage() {
  cat <<EOF
Usage: $0 [-h] -e ENV -r REGION
EOF
  exit 1
}

main() {
  local env="${1:?ENV required}"
  log "Deploy to $env"
}

main "$@"

Переменные

NAME="World"
echo "$NAME"
echo "${NAME}"
echo "${NAME:-default}"        # если пусто → "default"
echo "${NAME:?error msg}"      # если пусто → exit с ошибкой
echo "${#NAME}"                # длина
echo "${NAME/o/0}"             # замена первого o → 0
echo "${NAME//o/0}"            # замена всех
echo "${NAME^^}"               # UPPERCASE
echo "${NAME,,}"               # lowercase

# Параметры скрипта
"$0"   # имя скрипта
"$1"   # первый аргумент
"$@"   # все аргументы (массив)
"$*"   # все как одна строка
"$#"   # количество
"$?"   # exit code последней команды
"$$"   # PID скрипта
"$!"   # PID последнего фонового процесса

Условия

if [[ -f "file.txt" ]]; then
  echo "файл"
elif [[ -d "dir" ]]; then
  echo "директория"
else
  echo "нет"
fi

# Тесты файлов
-f file       обычный файл
-d dir        директория
-e path       существует
-r/-w/-x      readable/writable/executable
-s file       не пустой
-L file       symlink

# Строки
-z "$str"     пусто
-n "$str"     не пусто
"$a" == "$b"  равны
"$a" != "$b"
"$str" =~ ^prod regex

# Числа
-eq -ne -lt -le -gt -ge

# Арифметика
((x = 1 + 2))
((x > 5)) && echo "more than 5"

Циклы

for f in *.log; do
  echo "$f"
done

for i in {1..10}; do echo $i; done
for i in {0..20..2}; do echo $i; done   # step 2

for ((i=0; i<10; i++)); do echo $i; done

while [[ $i -lt 10 ]]; do
  ((i++))
done

# Чтение файла по строкам
while IFS= read -r line; do
  echo "$line"
done < file.txt

# break, continue
for i in 1 2 3; do
  [[ $i -eq 2 ]] && continue
  [[ $i -eq 3 ]] && break
  echo $i
done

case

case "$ENV" in
  prod|production)
    echo "production"
    ;;
  dev|development)
    echo "dev"
    ;;
  *)
    echo "unknown"
    exit 1
    ;;
esac

Функции

greet() {
  local name="${1:-friend}"
  echo "Hello, $name"
  return 0
}

greet "Ilya"
result=$(greet "Ilya")            # перехват вывода

# Множественные возвраты — через массив или stdout
get_user_info() {
  echo "alice"
  echo "30"
}

read -r name age < <(get_user_info)

Массивы

arr=("a" "b" "c")
echo "${arr[0]}"           # a
echo "${arr[@]}"           # все
echo "${#arr[@]}"          # длина
arr+=("d")                 # добавить

# Iterate
for x in "${arr[@]}"; do echo "$x"; done

# Associative (bash 4+)
declare -A user
user[name]="Alice"
user[age]=30
echo "${user[name]}"
for k in "${!user[@]}"; do echo "$k=${user[$k]}"; done

Heredocs

cat <<EOF > config.yaml
key: value
nested:
  - item1
  - item2
EOF

# Без раскрытия переменных
cat <<'EOF' > script.sh
echo "$HOME — НЕ раскрывается"
EOF

# Со стрипом отступов
cat <<-EOF
        отступ tab будет удалён
EOF

Pipes и process substitution

ls | grep .log | wc -l

# Pipefail
set -o pipefail   # exit code = первая ошибка в pipe

# Process substitution
diff <(cmd1) <(cmd2)

# Process group для несколько процессов
{ cmd1; cmd2; } > out.log

Traps

tmpfile=$(mktemp)
trap 'rm -f "$tmpfile"' EXIT
trap 'echo "Interrupted"; exit 130' INT TERM
trap 'echo "Error on line $LINENO"' ERR

Парсинг аргументов

# Простой
while [[ $# -gt 0 ]]; do
  case "$1" in
    -e|--env) ENV="$2"; shift 2 ;;
    -h|--help) usage; exit 0 ;;
    *) echo "Unknown: $1"; exit 1 ;;
  esac
done

# getopts (только короткие флаги)
while getopts "e:r:h" opt; do
  case $opt in
    e) ENV="$OPTARG" ;;
    r) REGION="$OPTARG" ;;
    h) usage; exit 0 ;;
    *) usage; exit 1 ;;
  esac
done

Полезные паттерны

# Проверка установлен ли инструмент
if ! command -v jq &> /dev/null; then
  echo "jq required"
  exit 1
fi

# Тихий exit от команд
git status &> /dev/null

# Retry
for i in {1..5}; do
  curl -f https://api.com && break || sleep 2
done

# Параллельные команды
cmd1 & cmd2 & wait

# Timeout
timeout 30s long-running-command

# Lock files (предотвратить повторный запуск)
exec 200> /tmp/myscript.lock
flock -n 200 || { echo "Already running"; exit 1; }

Безопасность

Документация