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; }
Безопасность
- Всегда цитируй переменные:
"$var"
- Никогда
eval $user_input
shellcheck script.sh — обязательная привычка
- Для секретов:
read -s или окружение
-- разделитель: rm -- "$file" (защита если имя начинается с -)
Документация