10 thủ thuật Bash mà các kỹ sư DevOps học được từ trải nghiệm thực tế
Hầu hết các kỹ sư DevOps đều tiếp cận Bash scripting bằng cách nhìn các kỹ sư kỳ cựu gõ một cách thoải mái ba ký tự mà vô tình khởi động lại cả một cluster.
Sự thật là đó không phải là cách tốt nhất để giúp hệ thống cơ sở hạ tầng hoạt động ổn định. Nhiều khi những thủ thuật đó có thể bị lỗi gây ra những hậu quả. Dưới đây là 10 thủ thuật được đúc kết từ trải nghiệm thực tế mà mọi kỹ sư DevOps không thể bỏ qua.
1. Đặt Script
Cảnh báo: Script triển khai của bạn gặp lỗi ở dòng 12, nhưng vẫn tiếp tục chạy đến dòng 156. Nó có thể được ví dụ như một chiếc xe say rượu sẽ phá hủy mọi thứ trên đường đi.
Cách khắc phục: set -euo pipefail
Dòng lệnh này thực hiện 3 việc:
-e thoát ngay lập tức nếu lệnh nào đó thất bại.
-u coi các biến chưa được gán là lỗi.
-o pipefail khiến pipelining thất bại nếu bất kỳ lệnh nào trong pipeline thất bại, không chỉ lệnh cuối cùng.
2. Hãy ngừng mã hóa cứng các đường dẫn
Cảnh báo: Script của bạn chạy tốt trên laptop nhưng lại chết ngay lập tức trong CI vì thư mục /Users/you/projects không tồn tại trên máy chủ build.
Cách khắc phục:
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
cd "$SCRIPT_DIR" || exit
Dòng lệnh này giúp tìm thư mục của script bất kể từ đâu nó được gọi. Script của bạn trở nên di động. Các đồng đội của bạn cũng sẽ ngừng gửi những tin nhắn phàn nàn gián tiếp trên Slack.
3. Sử dụng trap để dọn dẹp lỗi
Cảnh báo: Script của bạn tạo ra các tệp tạm thời, rồi gặp sự cố. Giờ bạn có 47 thư mục tạm ăn hết không gian đĩa trên môi trường sản xuất.
Cách khắc phục:
cleanup() {
rm -rf "$TEMP_DIR"
}
trap cleanup EXIT
trap chạy hàm dọn dẹp của bạn ngay cả khi script gặp lỗi. Đây là cơ chế thu gom rác tự động cho các shell script. Dùng nó để dọn tệp tạm, các tiến trình nền hoặc bất cứ thứ gì cần bị hủy khi script kết thúc.
4. Lặp qua các tập tin mà không cần tách chuỗi khi gặp khoảng trắng
Cảnh báo: Bạn viết for file in $(ls *.log) và nó hoạt động tốt cho đến khi ai đó đặt tên tệp là "production backup.log", và script của bạn xử lý nó như hai tệp riêng biệt.
Cách khắc phục:
for file in *.log; do
echo "Processing $file"
done
Sử dụng globbing, chúng sẽ xử lý được các khoảng trắng, dòng mới và những tên tệp kỳ quái mà đồng nghiệp của bạn sẽ tạo ra.
5. Kiểm tra xem các lệnh có tồn tại trước khi chạy chúng
Cảnh báo: Script giả định rằng jq đã được cài đặt. Nếu không, logic phân tích JSON của bạn sẽ gặp lỗi và việc triển khai thất bại.
Cách khắc phục:
if ! command -v jq &> /dev/null; then
echo "jq is required but not installed"
exit 1
fi
Dòng này kiểm tra xem một lệnh có tồn tại hay không trước khi bạn sử dụng nó. Hãy thêm những kiểm tra này vào đầu script của bạn.
6. Sử dụng ${VAR:-default} cho các giá trị mặc định hợp lý
Cảnh báo: Bạn cần một biến môi trường tùy chọn, nhưng không muốn viết 15 dòng logic if-else.
Cách khắc phục:
ENVIRONMENT=${ENVIRONMENT:-staging}
Nếu $ENVIRONMENT đã được gán giá trị, hãy sử dụng nó. Nếu không, mặc định là staging. Một dòng, không có drama.
Cũng có ${VAR:=default}, cái này gán giá trị cho biến nếu nó rỗng, và ${VAR:?error message} sẽ thoát với thông báo lỗi nếu biến chưa được gán giá trị.
7. Thu thập kết quả đầu ra và mã lỗi của lệnh
Cảnh báo: Bạn muốn kiểm tra xem lệnh có thành công hay không và lấy đầu ra của nó. Bạn sẽ gặp rắc rối với logic lồng nhau lộn xộn.
Cách khắc phục:
output=$(kubectl get pods 2>&1) || {
echo "kubectl failed: $output"
exit 1
}
Hoặc bạn có thể viết:
kubectl get pods
exit_code=$?
if [ $exit_code -ne 0 ]; then
echo "kubectl failed with exit code $exit_code"
exit 1
fi
$? chứa mã lỗi của lệnh cuối cùng. Hãy sử dụng nó khi bạn cần kiểm soát chi tiết hơn là chỉ dùng && hoặc ||.
8. Đọc từng dòng trong tập tin mà không ngắt dòng
Cảnh báo: Bạn thử đọc tệp for line in $(cat file.txt) và nó tách các dòng theo khoảng trắng thay vì theo dấu phân cách dòng.
Cách khắc phục:
while IFS= read -r line; do
echo "$line"
done < file.txt
Cách này đọc tệp dòng theo dòng, giữ nguyên khoảng trắng. IFS= ngăn không cho xóa khoảng trắng đầu/cuối. -r ngăn không cho dấu gạch chéo ngược (\) được hiểu như ký tự escape.
9. Sử dụng mảng cho các lệnh phức tạp
Cảnh báo: Bạn đang xây dựng một lệnh Docker với các cờ tùy chọn, và việc nối chuỗi trông như một mớ bòng bong.
Cách khắc phục:
docker_args=(
"run"
"-d"
"--name" "my-app"
)
if [ "$PROD" = "true" ]; then
docker_args+=("--restart" "always")
fi
docker "${docker_args[@]}"
Mảng giúp bạn xây dựng các lệnh từng phần mà không phải lo lắng về vấn đề trích dẫn. "${array[@]}" mở rộng tất cả phần tử thành các đối số riêng biệt.
10. Gỡ lỗi Script với lệnh set -x
Cảnh báo: Script của bạn gặp lỗi và bạn không biết dòng nào gây ra lỗi hoặc giá trị các biến là gì.
Cách khắc phục:
set -x # Bật chế độ gỡ lỗi
# Mã của bạn gặp vấn đề ở đây
set +x # Tắt chế độ gỡ lỗi
Hoặc bạn có thể chạy script với bash -x script.sh. Mỗi lệnh sẽ được in ra trước khi thực thi, với tất cả các biến đã được mở rộng. Nó giống như console.log cho shell scripts.
Khi gỡ lỗi vào ban đêm, điều này thường là sự khác biệt giữa việc sửa lỗi trong 5 phút hoặc mất một giờ để thêm các câu lệnh echo.
Kết luận
Chúng ta phải biết rằng Bash sẽ không biến mất. Trong khi bạn đang thiết kế các cụm Kubernetes và xây dựng các đường dẫn CI/CD, bạn vẫn sẽ dành hàng giờ để viết các kịch bản shell để kết nối mọi thứ lại với nhau.
Tuy nhiên những thủ thuật này sẽ giúp bạn làm việc nhanh hơn, các kịch bản của bạn đáng tin cậy hơn và các phiên gỡ lỗi của bạn ngắn hơn. Ngoài ra, bạn nên giữ một tệp chứa các đoạn mã mẫu phía trên để có thể tiện sử dụng cho sau này.




















