Shell Scripting: Tự Động Hóa Bằng Bash
Khi bạn gõ cùng một chuỗi lệnh nhiều lần, đã đến lúc viết script. Shell script gom các lệnh (chính những lệnh ta học cả series) thành một file chạy được, thêm biến, điều kiện, vòng lặp — biến thao tác thủ công thành tự động. Đây là kỹ năng biến "biết lệnh" thành "tự động hóa được".
Một script tối thiểu
Script chỉ là một file văn bản chứa các lệnh. Tạo hello.sh:
#!/usr/bin/env bash
echo "Xin chao tu script"
Dòng đầu #!/usr/bin/env bash là shebang — nó báo cho hệ thống chạy file này bằng bash. (Có thể viết #!/bin/bash; dùng env bash linh hoạt hơn vì tìm bash theo PATH.)
Cho file quyền chạy (nhớ Bài 7) rồi chạy:
chmod +x hello.sh
./hello.sh
./ cần thiết vì thư mục hiện tại không nằm trong PATH. Cách khác: bash hello.sh (không cần chmod +x).
Biến
name="Nghia" # KHÔNG có dấu cách quanh dấu =
echo "$name" # dùng giá trị: thêm $
echo "${name}_dev" # ngoặc nhọn khi cần tách rõ tên biến
Lỗi kinh điển của người mới: viết name = "Nghia" (có dấu cách) — sai, bash hiểu nhầm là chạy lệnh name. Quy tắc: không dấu cách quanh =.
Luôn để biến trong ngoặc kép khi dùng: "$name". Không có ngoặc kép, biến chứa dấu cách hoặc ký tự đặc biệt sẽ vỡ ra — nguồn của vô số bug script.
Lấy kết quả của một lệnh vào biến (command substitution):
today=$(date +%F)
echo "Hom nay la $today"
files=$(ls | wc -l)
Tham số dòng lệnh
Script nhận tham số khi chạy (./script.sh arg1 arg2):
echo "Tham so 1: $1" # tham số đầu tiên
echo "Tham so 2: $2"
echo "Tat ca: $@" # tất cả tham số
echo "So luong: $#" # số tham số
echo "Ten script: $0"
Đặt giá trị mặc định khi tham số trống:
name="${1:-the gioi}" # nếu $1 rỗng, dùng "the gioi"
Điều kiện: if
if [ -f /etc/os-release ]; then
echo "co file"
elif [ -d /tmp ]; then
echo "co thu muc tmp"
else
echo "khong co"
fi
[ ... ] là lệnh test. Các kiểm tra hay dùng:
File: -f (file tồn tại) -d (thư mục) -e (tồn tại) -r/-w/-x (quyền)
Chuỗi: "$a" = "$b" (bằng) -z "$a" (rỗng) -n "$a" (không rỗng)
Số: -eq (=) -ne (≠) -lt (<) -le (≤) -gt (>) -ge (≥)
Lưu ý so sánh số dùng -eq/-lt..., so sánh chuỗi dùng =. (Bash còn có [[ ... ]] mạnh hơn, hỗ trợ &&, ||, so khớp mẫu — nên dùng [[ ]] trong script bash.)
Vòng lặp: for, while
# for: duyệt danh sách
for i in 1 2 3; do
echo "lan $i"
done
# for: duyệt file (rất hay dùng)
for f in *.txt; do
echo "xu ly $f"
done
# while: lặp tới khi điều kiện sai
n=0
while [ $n -lt 3 ]; do
echo "n = $n"
n=$((n + 1)) # tính toán số học trong $(( ))
done
for f in *.txt kết hợp với wildcard (Bài 3) là mẫu xử lý hàng loạt file rất phổ biến trong script vận hành.
Hàm
Gom logic dùng lại thành hàm:
greet() {
echo "Xin chao, $1" # $1 là tham số của HÀM (không phải của script)
}
greet "Linux"
greet "DevOps"
Hàm nhận tham số qua $1, $2... giống script. Đặt hàm trước chỗ gọi.
Exit code: thành công hay thất bại
Mỗi lệnh trả về một exit code: 0 = thành công, khác 0 = lỗi. Đây là cách script biết một lệnh có chạy được không.
ls /tmp
echo $? # in exit code của lệnh vừa rồi (0 nếu OK)
if grep -q "root" /etc/passwd; then
echo "tim thay" # if dựa trên exit code: grep thành công (0) = tìm thấy
fi
&& chạy lệnh sau nếu lệnh trước thành công; || chạy nếu thất bại:
mkdir /tmp/x && echo "tao xong" || echo "tao that bai"
Cho script tự kết thúc với một exit code:
exit 0 # báo thành công
exit 1 # báo lỗi
Viết script an toàn: set -euo pipefail
Đặt dòng này ngay sau shebang trong mọi script nghiêm túc:
#!/usr/bin/env bash
set -euo pipefail
-e— dừng ngay khi một lệnh thất bại (thay vì chạy tiếp với trạng thái hỏng). Kiểm chứng: một scriptset -egặpls /khong-ton-taisẽ dừng tại đó, không chạy các dòng sau.-u— báo lỗi khi dùng biến chưa khai báo (bắt lỗi gõ sai tên biến).-o pipefail— pipeline thất bại nếu bất kỳ lệnh nào trong pipe lỗi (mặc định chỉ xét lệnh cuối).
Ba cờ này biến script "âm thầm chạy tiếp dù hỏng" thành "dừng và báo ngay" — tránh được rất nhiều sự cố khó hiểu. Đây là dấu hiệu của một script viết cẩn thận.
Một script hoàn chỉnh
Ghép lại — script backup một thư mục thành file .tar.gz có ngày tháng (dùng kiến thức Bài 9):
#!/usr/bin/env bash
set -euo pipefail
src="${1:?Can duong dan thu muc can backup}" # bắt buộc có tham số 1
dest="/backup"
stamp=$(date +%Y%m%d-%H%M%S)
name="$(basename "$src")-$stamp.tar.gz"
mkdir -p "$dest"
tar -czf "$dest/$name" "$src"
echo "Da backup $src -> $dest/$name"
${1:?thông báo} bắt buộc phải có tham số, không thì script dừng kèm thông báo. Đây là kiểu script bạn sẽ viết nhiều — và Bài 17 sẽ cho nó chạy tự động theo lịch.
🧹 Dọn dẹp
cd /tmp && rm -f hello.sh demo.sh fail.sh
Tổng kết
Shell script gom lệnh thành file chạy được: shebang + chmod +x, biến (không dấu cách quanh =, luôn "$var"), tham số ($1, $@, ${1:-default}), điều kiện (if [[ ]], test file/chuỗi/số), vòng lặp (for, while), hàm, và exit code (0 = OK, $?, &&/||). Luôn mở đầu bằng set -euo pipefail để script dừng khi gặp lỗi. Đây là công cụ biến các lệnh rời rạc thành tự động hóa.
Script viết xong, nhưng vẫn phải chạy tay. Bài cuối (17) cho chúng chạy tự động theo lịch bằng cron — và tổng kết cả series.