Debug script trong Bash: Tìm và sửa lỗi hiệu quả cho DevOps
Hướng dẫn kỹ thuật debug Bash script cho DevOps: bash -x, bash -n, set -euo pipefail, tùy biến PS4, bắt lỗi với trap ERR và phân tích tĩnh với ShellCheck.
Debug script trong Bash cho DevOps
Ở bài trước chúng ta đã làm quen với việc lên lịch chạy script tự động bằng cron. Khi một script được đưa lên server để chạy nền hoặc tích hợp vào pipeline CI/CD, việc script phát sinh lỗi mà không để lại dấu vết rõ ràng là một trong những nguyên nhân phổ biến nhất gây gián đoạn hệ thống.
Khác với các ngôn ngữ biên dịch hoặc có runtime phong phú (như Go, Python), Bash script theo mặc định có xu hướng tiếp tục chạy ngay cả khi một câu lệnh bị lỗi. Một biến bị gõ sai tên, một đường dẫn không tồn tại hay một pipe trả về mã lỗi âm thầm đều có thể biến một script dọn dẹp đơn giản thành thảm họa xóa nhầm dữ liệu.
Bài viết này tổng hợp toàn bộ các kỹ thuật debug từ cơ bản đến nâng cao trong Bash: từ kiểm tra cú pháp khô (dry-run), theo dõi dấu vết thực thi (execution tracing), tùy biến định dạng trace với PS4, kích hoạt chế độ nghiêm ngặt (Strict Mode), bắt lỗi với trap ERR, cho đến kiểm tra tự động bằng ShellCheck.
Kiểm tra cú pháp nhanh với cờ bash -n
Trước khi chạy thử một script có thể làm thay đổi hệ thống (như ghi đè file hay gọi API xóa tài nguyên), bạn luôn nên kiểm tra cú pháp trước bằng cờ -n (noexec).
| |
Cờ -n chỉ đọc và parse toàn bộ file để kiểm tra cấu trúc cú pháp (như thiếu từ khóa fi, quên đóng ngoặc }, cú pháp case...esac sai) mà hoàn toàn không thực thi bất kỳ câu lệnh nào.
Ví dụ với đoạn script có lỗi quên đóng ngoặc:
| |
Khi chạy kiểm tra cú pháp:
| |
Trong quy trình CI/CD, bạn có thể thêm một bước kiểm tra toàn bộ file .sh trong repository bằng lệnh:
| |
Tracing thực thi từng bước với bash -x và set -x
Kỹ thuật debug phổ biến nhất trong Bash là Execution Tracing với cờ -x (xtrace). Khi bật cờ này, Bash sẽ in ra từng lệnh sau khi đã hoàn thành việc mở rộng biến (parameter expansion), thay thế lệnh (command substitution) và tách từ (word splitting), ngay trước khi lệnh đó được thực thi.
Cách 1: Chạy toàn bộ script ở chế độ trace
| |
Hoặc thêm cờ -x trực tiếp vào dòng Shebang đầu file:
| |
Cách 2: Bật tắt trace có chọn lọc trong script
Nếu script dài hàng trăm dòng và bạn chỉ muốn theo dõi một đoạn logic cụ thể, hãy dùng cặp lệnh set -x (bật trace) và set +x (tắt trace):
| |
Khi thực thi, chỉ phần nằm giữa set -x và set +x mới in chi tiết từng dòng lệnh với tiền tố dấu +:
| |
Nâng cấp đầu ra debug chuyên nghiệp với biến PS4
Theo mặc định, khi bật -x, Bash chỉ in dấu + trước mỗi dòng lệnh. Khi script gọi nhiều function hoặc source nhiều file khác nhau, rất khó để biết lệnh đó nằm ở file nào, hàm nào và dòng bao nhiêu.
Biến môi trường PS4 (Prompt String 4) cho phép bạn tùy biến định dạng tiền tố in ra khi debug. Các biến hữu ích gồm:
| Ký hiệu / Biến | Ý nghĩa |
|---|---|
$0 hoặc ${BASH_SOURCE[0]} | Tên file script hiện tại |
$LINENO | Số dòng đang thực thi |
${FUNCNAME[0]} | Tên function đang chạy |
+ | Dấu cộng tăng theo độ sâu subshell |
Bạn có thể cấu hình PS4 ngay đầu script hoặc truyền trực tiếp từ dòng lệnh:
| |
Ví dụ kiểm chứng:
| |
Kết quả in ra cực kỳ rõ ràng, kèm chính xác tên hàm và số dòng:
| |
Chế độ nghiêm ngặt trong Bash (Strict Mode)
Trong môi trường DevOps, hầu hết các lỗi nghiêm trọng xuất phát từ việc script âm thầm bỏ qua lỗi và tiếp tục chạy. Hãy đưa dòng cấu hình Strict Mode tiêu chuẩn vào đầu mọi script:
| |
Ý nghĩa chi tiết của từng tùy chọn:
set -e(errexit): Dừng script ngay lập tức nếu bất kỳ câu lệnh đơn lẻ nào trả về exit code khác 0.set -u(nounset): Báo lỗi và dừng script ngay nếu truy cập vào một biến chưa được định nghĩa (ngăn chặn các lỗi tai hại nhưrm -rf "$UNSET_VAR/*"biến thànhrm -rf "/*").set -o pipefail: Theo mặc định, pipelinecmd1 | cmd2 | cmd3chỉ trả về exit code củacmd3. Tùy chọn này đảm bảo nếucmd1hoặccmd2bị lỗi, toàn bộ pipeline sẽ trả về mã lỗi của lệnh thất bại đầu tiên.IFS=$'\n\t': Đặt ký tự phân tách trường nội bộ chỉ gồm dấu xuống dòng và tab, tránh lỗi chia tách từ ngoài ý muốn khi biến chứa khoảng trắng.
Cạm bẫy khi dùng set -e và cách xử lý
Khi dùng set -e, nếu một lệnh kiểm tra dự kiến có thể trả về exit code khác 0 (ví dụ grep không tìm thấy dòng nào trả về 1), script sẽ bị dừng ngoài ý muốn. Cách xử lý đúng:
| |
Bắt lỗi và in Stack Trace với trap ERR
Để script tự động ghi nhận ngữ cảnh khi gặp sự cố, bạn có thể kết hợp trap với tín hiệu ERR. Khi bất kỳ lệnh nào bị lỗi, hàm xử lý lỗi sẽ được gọi tự động và in ra dấu vết stack trace gồm file, tên hàm và số dòng.
| |
Khi chạy script trên, ngay khi lệnh ls thất bại, hệ thống sẽ in ra bảng thông báo lỗi cùng stack trace chuẩn xác:
| |
Phân tích tĩnh tự động với ShellCheck
ShellCheck là công cụ phân tích tĩnh (linter) mã nguồn Bash mạnh mẽ và phổ biến nhất hiện nay. ShellCheck có thể phát hiện hàng trăm lỗi tiềm ẩn: quên bọc ngoặc kép biến, sai cú pháp điều kiện, dùng sai lệnh nội tại, lỗi tính tương thích POSIX, v.v.
Cài đặt ShellCheck
| |
Chạy kiểm tra script
| |
Ví dụ một đoạn script chứa lỗi biến không được bọc ngoặc kép:
| |
ShellCheck sẽ lập tức chỉ ra cảnh báo nguy cơ:
| |
Tích hợp ShellCheck vào Git Pre-commit Hook hoặc pipeline CI giúp toàn bộ script trong team luôn đạt chuẩn chất lượng trước khi được merge vào branch chính.
Ví dụ thực hành: Script triển khai ứng dụng an toàn
Dưới đây là một script DevOps hoàn chỉnh kết hợp đầy đủ: kiểm tra cú pháp, Strict Mode, tùy biến PS4, bắt lỗi trap ERR và kiểm tra tham số đầu vào.
| |
Chạy script với cờ debug:
| |
Ghi chú triển khai
- Bảo vệ Secret khi bật debug: Cờ
set -xsẽ in toàn bộ giá trị của biến ra màn hình/log. Nếu script của bạn có xử lý password, token hoặc API key, hãy nhớ dùngset +xđể tắt trace trước khi đọc secret và chỉ bật lạiset -xsau khi hoàn tất. - Tách riêng luồng Log Debug: Trong production hoặc CI/CD, bạn có thể chuyển hướng đầu ra của xtrace (luồng file descriptor 2 / stderr) vào một file log riêng biệt để dễ kiểm tra lại:
BASH_XTRACEFD=3 bash -x script.sh 3> /var/log/script-trace.log. - Luôn kiểm tra ShellCheck trong CI: Thêm một step chạy
shellchecktrong GitHub Actions hoặc GitLab CI để ngăn chặn các lỗi sơ đẳng trước khi mã nguồn đến tay người dùng. - Không lạm dụng
set -emà thiếutrap:set -egiúp dừng script khi lỗi, nhưng nếu không có hàm cleanup đi kèm, script có thể để lại các file lock, thư mục tạm hoặc tài nguyên treo trên hệ thống.
Lời kết
Debug là một kỹ năng không thể thiếu để biến các script Bash từ những đoạn mã tự phát thành các công cụ tự động hóa chuẩn mực, đáng tin cậy trong môi trường DevOps. Bằng cách kết hợp bash -n, set -x, tùy biến PS4, kích hoạt Strict Mode và kiểm tra với ShellCheck, bạn có thể kiểm soát và xử lý triệt để mọi lỗi phát sinh.
Ở bài tiếp theo, chúng ta sẽ đi sâu vào Bài 10 — Error Handling trong Bash: tìm hiểu chi tiết các chiến lược bắt lỗi, cơ chế thử lại (retry pattern) và dọn dẹp tài nguyên tự động.
