Sau bài này bạn làm được: viết (hoặc review) script Bash cho vận hành mà không nuốt lỗi im lặng; hiểu vì sao set -u làm lộ bug sớm; dùng trap để dọn dẹp temp file; đọc log systemd/cron khi script “chạy mà không ra việc”.


Shell không phải ngôn ngữ ứng dụng — nhưng nó là keo dán của infra

Phần lớn pipeline CI, cloud-init, và “fix nhanh” trên server đều qua shell. Bash đủ mạnh để gây họa: xoá nhầm path, curl | bash từ nguồn không tin cậy, hoặc biến rỗng biến rm -rf $DIR/* thành thảm hoạ.

Nguyên tắc: script ops nên nhỏ, tường minh, và fail fast — còn logic phức tạp thì chuyển sang ngôn ngữ có kiểu và test (Go, Python…).


set -euo pipefail — bộ ba mặc định nên cân nhắc

#!/usr/bin/env bash
set -euo pipefail
  • -e thoát khi lệnh trả non-zero. Trade-off: với grep, exit code 1 khi không có dòng khớp là bình thường — cần bọc if/then hoặc bỏ qua exit một cách có chủ đích.
  • -u thoát khi mở rộng biến chưa được gán. Trade-off: gần như luôn là điểm cộng; cứu bạn khỏi typo kiểu HOM thay vì HOME.
  • -o pipefail pipeline fail nếu bất kỳ stage nào fail. Trade-off: ví dụ chạy false rồi nối pipeline rồi true vẫn được coi là lỗi (đúng cho ops).

Ví dụ pipefail thực tế:

set -euo pipefail
# Không có pipefail: chỉ status của tar được xét; gzip lỗi có thể bị bỏ qua
some_backup_dir=/var/backups/app
tar -cf - "$some_backup_dir" | gzip -9 > /tmp/backup.tgz

Quoting: nơi 80% bug shell sinh ra

foo="hello world"
# Sai: tạo hai argument
echo $foo
# Đúng: một argument
echo "$foo"

Rule of thumb: mặc định dùng "$var" — chỉ bỏ quote khi bạn cố ý word-split (hiếm trong ops).

Path có space: luôn quote biến chứa path. Nếu bạn nghĩ “path trên server không có space” — volume mount từ Windows/Mac qua NFS đã từng chứng minh điều ngược lại.


read và while read — IFS và subshell

# Đọc dòng an toàn hơn (không trim whitespace mặc định theo cách khó đoán)
while IFS= read -r line; do
  printf '%s\n' "$line"
done < input.txt

Lỗi kinh điển: cat file | while read …; do …; done — body while chạy trong subshell nếu pipeline, biến đếm bên trong không ra ngoài được. Dùng process substitution hoặc đổi cấu trúc:

while IFS= read -r line; do
  : # ...
done < input.txt

trap — dọn dẹp khi script bị kill hoặc lỗi

#!/usr/bin/env bash
set -euo pipefail
tmp=$(mktemp)
trap 'rm -f "$tmp"' EXIT INT TERM

# ... làm việc với $tmp ...

Lưu ý: trap chạy khi shell nhận signal; không thay thế được việc transaction — nếu bạn cần “all or nothing” trên DB, hãy dùng cơ chế DB.


Cron và systemd: môi trường “nghèo”

Cron job không load .bashrc. Một job “chạy được trên tay” nhưng fail trong cron thường vì:

  • PATH thiếu
  • HOME không như bạn nghĩ
  • locale / timezone khác interactive shell
# Cron: luôn set PATH và shell rõ ràng
SHELL=/bin/bash
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
0 * * * * root /usr/local/bin/backup.sh >> /var/log/backup.log 2>&1

systemd service nên khai báo User=, WorkingDirectory=, Environment= rõ — bài 5 đi sâu.


[[ vs [ và test chuỗi

# [[ hỗ trợ pattern matching và ít surprise hơn cho string
if [[ "$status" == ok ]]; then
  echo fine
fi

# -z / -n cho empty check
if [[ -n "${VAR:-}" ]]; then
  echo VAR is set and non-empty
fi

curl | bash — vì sao team vẫn làm và vì sao SRE lo

  • Rủi ro: MITM, mirror độc hại, script thay đổi giữa audit và chạy.
  • Giảm thiểu: pin checksum, dùng package đã ký, hoặc vendor script qua artifact nội bộ.

Đừng đạo đức hoá quá mức — hãy đo rủi ro theo môi trường: lab cá nhân khác production chứa PCI.


Shellcheck và format

  • Cài shellcheck trên máy dev hoặc CI; nó bắt nhiều footgun cổ điển.
  • shfmt (nếu team đồng ý) giúp diff dễ đọc.

Liên hệ bài trước / sau


Câu hỏi hay gặp

1. “Có nên #!/bin/sh hay #!/usr/bin/env bash?”
Nếu bạn dùng [[, source thay vì . theo style bash-only — hãy ghi bash. sh trên Debian thường là dash, nhanh nhưng khác bash.

2. “set -e làm grep của tôi fail?”
grep trả 1 khi không có match — đó là thành công logic trong nhiều script. Viết grep -q … || true hoặc if grep -q …; then.

3. “Khi nào nên bỏ shell chuyển Python?”
Khi bạn cần cấu trúc dữ liệu phức tạp, JSON parsing an toàn, hoặc unit test — shell vẫn có thể gọi python3 - <<'PY' … PY.


Bài tiếp theo trong loạt

Phần 4: User, group, permission và sudo — từ chmod 777 tới mô hình least privilege thực tế.