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ớigrep, exit code 1 khi không có dòng khớp là bình thường — cần bọcif/thenhoặ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ểuHOMthay vìHOME.-o pipefail— pipeline fail nếu bất kỳ stage nào fail. Trade-off: ví dụ chạyfalserồi nối pipeline rồitruevẫ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ì:
PATHthiếuHOMEkhô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
shellchecktrê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ế.