Sổ tay kỹ thuật

Bash scripting – pattern an toàn hay dùng

cheatsheet#linux#bash#shell· cập nhật 2026-11-06

Tra nhanh khung script bash an toàn, trap, getopts, vòng lặp, xử lý chuỗi và mảng khi viết script vận hành.

Sửa trang này
Mục lục

Script vận hành nên bắt đầu bằng strict mode, kiểm cú pháp bằng bash -n script.sh và lint bằng shellcheck script.sh trước khi chạy.

Khung script

backup-configs.sh
#!/usr/bin/env bash
set -euo pipefail
 
usage() {
  echo "Usage: $0 [-n] [-o outdir] -f hosts.txt" >&2
  exit 2
}
 
log() { printf '%s %s\n' "$(date -Is)" "$*" >&2; }
 
DRY_RUN=0
OUTDIR="./out"
HOSTS_FILE=""
 
while getopts ":nf:o:h" opt; do
  case "$opt" in
    n) DRY_RUN=1 ;;
    f) HOSTS_FILE="$OPTARG" ;;
    o) OUTDIR="$OPTARG" ;;
    h) usage ;;
    :) echo "Thiếu giá trị cho -$OPTARG" >&2; usage ;;
    \?) echo "Option không hợp lệ: -$OPTARG" >&2; usage ;;
  esac
done
shift $((OPTIND - 1))
[[ -n "$HOSTS_FILE" ]] || usage
 
TMPDIR_RUN="$(mktemp -d)"
cleanup() { rm -rf -- "$TMPDIR_RUN"; }
trap cleanup EXIT
trap 'log "Lỗi tại dòng $LINENO: $BASH_COMMAND"' ERR
 
mkdir -p "$OUTDIR"
failed=()
while IFS= read -r host; do
  [[ -z "$host" || "$host" == \#* ]] && continue
  log "Backup $host"
  if (( DRY_RUN )); then continue; fi
  if ssh -n -o BatchMode=yes -o ConnectTimeout=10 "$host" 'show running-config' \
      > "$TMPDIR_RUN/$host.cfg"; then
    mv "$TMPDIR_RUN/$host.cfg" "$OUTDIR/$host-$(date +%F).cfg"
  else
    failed+=("$host")
  fi
done < "$HOSTS_FILE"
 
if (( ${#failed[@]} )); then
  log "Thất bại: ${failed[*]}"
  exit 1
fi

ssh trong vòng while read sẽ "nuốt" stdin và vòng lặp chỉ chạy 1 lần. Dùng ssh -n hoặc ssh ... < /dev/null.

Strict mode và lỗi

Mục đích Cú pháp
Dừng khi lệnh lỗi, biến chưa gán, lỗi trong pipe set -euo pipefail
Debug từng lệnh bash -x script.sh hoặc set -x / set +x
Cho phép một lệnh được lỗi cmd || true
Bắt lỗi mà không bị -e dừng if ! out=$(cmd 2>&1); then echo "$out"; fi
Dọn dẹp khi thoát (kể cả lỗi) trap cleanup EXIT
Bắt Ctrl+C trap 'echo dừng; exit 130' INT TERM
Biến bắt buộc, báo lỗi rõ : "${TOKEN:?Chưa đặt TOKEN}"

Biến và chuỗi

Mục đích Cú pháp
Giá trị mặc định ${OUTDIR:-./out}
Độ dài chuỗi ${#var}
Bỏ prefix ngắn nhất / dài nhất ${path#*/} / ${path##*/} (= basename)
Bỏ suffix ${file%.cfg} / ${path%/*} (= dirname)
Thay lần đầu / thay tất cả ${s/eth/Gi} / ${s//,/ }
Cắt chuỗi ${s:0:8}
Chữ thường / hoa ${s,,} / ${s^^}
Regex và lấy group [[ $ifname =~ ^([a-z]+)([0-9/]+)$ ]] && echo "${BASH_REMATCH[2]}"
So khớp glob [[ $host == pe* ]]
Tách theo dấu phẩy IFS=, read -r a b c <<< "$line"
Here-doc không expand biến cat <<'EOF' ... EOF (nháy đơn quanh EOF)

Mảng

Mục đích Cú pháp
Khai báo / thêm phần tử hosts=(pe1 pe2) / hosts+=(pe3)
Duyệt an toàn for h in "${hosts[@]}"; do ...; done
Số phần tử ${#hosts[@]}
Đọc file thành mảng mapfile -t hosts < hosts.txt
Mảng kết hợp declare -A asn=([pe1]=64500 [pe2]=64501); echo "${asn[pe1]}"
Duyệt key của mảng kết hợp for k in "${!asn[@]}"; do ...; done

Vòng lặp và song song

Mục đích Cú pháp
Đọc file từng dòng while IFS= read -r line; do ...; done < file
Dãy số for i in {1..10} / for ((i=0; i<n; i++))
Duyệt file có space trong tên find . -name '*.cfg' -print0 | while IFS= read -r -d '' f; do ...; done
Chạy song song 10 luồng xargs -P 10 -I{} ./backup-one.sh {} < hosts.txt
Chờ job nền cmd1 & cmd2 & wait
Retry với backoff for i in 1 2 3; do cmd && break; sleep $((i*5)); done
Giới hạn thời gian lệnh timeout 30 cmd

Kiểm tra

Mục đích Cú pháp
File tồn tại / thư mục / đọc được [[ -f f ]] / [[ -d d ]] / [[ -r f ]]
Chuỗi rỗng / không rỗng [[ -z $s ]] / [[ -n $s ]]
So sánh số (( n > 5 ))
Lệnh có tồn tại command -v jq >/dev/null || { echo "cần jq"; exit 1; }
Chạy với root (( EUID == 0 )) || { echo "cần sudo"; exit 1; }

Luôn đặt biến trong nháy kép ("$var", "${arr[@]}") để tránh tách từ và glob. Script dài hơn ~150 dòng hoặc cần parse JSON phức tạp thì chuyển sang Python. Xem thêm regex cheatsheet.