Kỹ Thuật Lập Trình Shell Script Trên Linux: Nguyên Lý Và Ứng Dụng Thực Tiễn

Bản chất và Vai trò của Trình bao (Shell)

Shell là một chương trình giao diện dòng lệnh hoạt động như cầu nối trung gian giữa người dùng (hoặc ứng dụng) và nhân hệ điều hành (kernel). Khi thực thi, nó nhận chuỗi ký tự đầu vào, phân tích cú pháp, sau đó triệu gọi các tiến trình hệ thống tương ứng. Ngoài khả năng điều khiển hệ thống, Shell còn hỗ trợ lập trình kịch bản (scripting), cho phép tự động hóa các tác vụ lặp lại.

Cấu trúc Script & Quy trình Thực thi

Một kịch bản Shell thực chất là tập hợp các lệnh hệ thống được sắp xếp tuần tự trong một tệp văn bản. Để hệ thống nhận diện và thực thi đúng, tệp bắt buộc phải chứa dòng Shebang ở đầu tiên.

#!/usr/bin/env bash
# Mô tả: Kịch bản kiểm tra trạng thái hệ thống
# Người phát triển: Admin
# Ngày tạo: 2023-10-15

echo "Đang khởi chạy kiểm tra tài nguyên..."

Quyền thực thi được cấp thông qua lệnh chmod +x ten_script.sh. Nếu không cấp quyền, có thể gọi trực tiếp trình thông dịch: bash ten_script.sh hoặc sh ten_script.sh.

Ký tự Đặc biệt, Chuyển hướng & Đường ống

Shell cung cấp bộ cú pháp mạnh mẽ để xử lý luồng dữ liệu và điều khiển ngữ cảnh:

  • #: Bắt đầu dòng chú thích, trình thông dịch bỏ qua.
  • $: Tiền tố trích xuất giá trị biến.
  • |: Toán tử ống (pipe), chuyển đầu ra lệnh trước thành đầu vào lệnh sau.
  • && / ||: Toán tử logic, thực thi điều kiện khi lệnh trước thành công hoặc thất bại.
  • ;: Ngăn cách nhiều lệnh trên cùng một dòng, thực thi độc lập kết quả.
  • >, >>: Chuyển hướng đầu ra ghi đè hoặc ghi nối tiếp.
  • 2>, 2>>: Chuyển hướng luồng lỗi (stderr).
  • &>: Chuyển hướng đồng thời stdout và stderr vào cùng một tệp.

Toán học trong Script

Các phép tính số nguyên có thể xử lý trực tiếp qua cú pháp $(( )) hoặc lệnh let. Đối với số thực, cần công cụ ngoài như bc.

#!/usr/bin/env bash
(( tong = 150 * 4 + 20 ))
echo "Tổng giá trị: $tong"

phan_tram=$(echo "scale=2; 45 / 100" | bc)
echo "Kết quả thực: $phan_tram"

Quản lý Biến & Mảng dữ liệu

Định nghĩa và Phạm vi

Biến được gán theo cấu trúc TEN_BIEN="gia_tri" (không có khoảng trắng quanh dấu =). Mặc định, biến chỉ tồn tại trong phiên làm việc hiện tại. Để mở rộng sang tiến trình con, sử dụng từ khóa export.

HOSTNAME="server_prod_01"
export DB_PORT=3306

Các biến môi trường hệ thống được lưu trong /etc/profile hoặc ~/.bashrc. Biến cục bộ trong hàm cần khai báo bằng local để tránh xung đột phạm vi.

Cấu trúc Mảng (Array)

Shell hỗ trợ mảng chỉ mục (indexed) và mảng kết hợp (associative). Dữ liệu được truy xuất qua chỉ số hoặc khóa tự định nghĩa.

#!/usr/bin/env bash
# Mảng chỉ mục
danh_sach_dich_vu=("nginx" "redis" "postgres" "docker")
echo "Dịch vụ thứ 2: ${danh_sach_dich_vu[1]}"

# Mảng kết hợp
declare -A cau_hinh_he_thong
cau_hinh_he_thong[cpu_cores]=8
cau_hinh_he_thong[ram_gb]=32
cau_hinh_he_thong[os]="Ubuntu 22.04"

# Duyệt mảng kết hợp
for khoa in "${!cau_hinh_he_thong[@]}"; do
    echo "$khoa => ${cau_hinh_he_thong[$khoa]}"
done

Cấu trúc Điều kiện & Toán tử Kiểm tra

Khối điều kiện sử dụng toán tử kiểm tra [[ ]] (nâng cao) hoặc [ ]. Các phép so sánh phổ biến:

  • Số học: -eq, -ne, -gt, -lt, -ge, -le
  • Chuỗi: =, !=, -z (rỗng), -n (không rỗng)
  • Tệp tin: -e (tồn tại), -d (thư mục), -f (tệp thường), -x (có quyền chạy)
#!/usr/bin/env bash
if [[ -d "/var/log/httpd" ]]; then
    echo "Thư mục nhật ký đã tồn tại."
elif [[ -f "/var/log/syslog" ]]; then
    echo "Chỉ tìm thấy nhật ký hệ thống."
else
    echo "Không tìm thấy đường dẫn mục tiêu."
fi

# Kiểm tra nhiều điều kiện cùng lúc
du_lieu_thieu=$(ls /data/backup/ 2>/dev/null | wc -l)
if [[ $du_lieu_thieu -gt 5 && $du_lieu_thieu -le 20 ]]; then
    echo "Số lượng bản sao lưu hợp lệ."
fi

Vòng lặp & Điều khiển Luồng thực thi

Shell hỗ trợ for, while và until để lặp lại tác vụ. Các từ khóa break (thoát vòng), continue (bỏ qua lượt hiện tại) và exit (kết thúc script) dùng để tinh chỉnh luồng.

Vòng lặp for (Danh sách & C-style)

#!/usr/bin/env bash
# Duyệt danh sách tệp
for tep_tin in /etc/*.conf; do
    if [[ -r "$tep_tin" ]]; then
        echo "Đang đọc: $tep_tin"
    fi
done

# Cú pháp C-style
for (( idx=0; idx<10; idx+=2 )); do
    echo "Bước lặp chẵn: $idx"
done

Vòng lặp while & until

#!/usr/bin/env bash
# While: chạy khi điều kiện ĐÚNG
so_nguoi_dung=1
while [[ $so_nguoi_dung -le 5 ]]; do
    echo "Người dùng số $so_nguoi_dung"
    (( so_nguoi_dung++ ))
done

# Until: chạy khi điều kiện SAI
trang_thai_mang=1
until [[ $trang_thai_mang -eq 0 ]]; do
    echo "Đang thử kết nối mạng..."
    sleep 2
    trang_thai_mang=$(( RANDOM % 2 )) # Mô phỏng kết quả ping
done

Câu lệnh case (Nhánh rẽ đa hướng)

#!/usr/bin/env bash
case "$1" in
    start)
        echo "Khởi động dịch vụ..."
        ;;
    stop|shutdown)
        echo "Đang dừng tiến trình..."
        ;;
    status)
        echo "Trạng thái: Hoạt động"
        ;;
    *)
        echo "Cú pháp: $0 {start|stop|status}"
        exit 1
        ;;
esac

Đóng gói Mã nguồn với Hàm (Functions)

Hàm cho phép tái sử dụng khối mã, giảm độ phức tạp và dễ bảo trì. Biến bên trong hàm mặc định là toàn cục, trừ khi khai báo local.

#!/usr/bin/env bash
ghi_log() {
    local muc_do="$1"
    local thong_diep="$2"
    local thoi_gian=$(date "+%Y-%m-%d %H:%M:%S")
    echo "[$thoi_gian] [$muc_do] $thong_diep" >> /var/app/runtime.log
}

ghi_log "WARN" "Dung lượng ổ đĩa vượt ngưỡng an toàn."
ghi_log "INFO" "Quét bảo trì định kỳ hoàn tất."

Tự động hóa Tương tác với Expect

expect là công cụ mở rộng Tcl/Expect, chuyên xử lý các yêu cầu nhập liệu tương tác (như mật khẩu SSH, xác nhận GPG) mà không cần can thiệp thủ công.

#!/usr/bin/env expect
# Đặt biến đầu vào
set dia_chi_ip [lindex $argv 0]
set tai_khoan [lindex $argv 1]
set mat_khau [lindex $argv 2]
set thoi_gian_cho 10

# Bật tương tác tự động
set timeout $thoi_gian_cho
spawn ssh -o StrictHostKeyChecking=no $tai_khoan@$dia_chi_ip

expect {
    "*yes/no" { send "yes\r"; exp_continue }
    "*assword:" { send "$mat_khau\r" }
}

# Giữ quyền điều khiển hoặc truyền lệnh
interact

Thực tiễn: Script Giám sát & Quản trị

Kiểm tra Trạng thái Máy chủ & Mạng

#!/usr/bin/env bash
# Ping liên tục 3 lần với khoảng nghỉ ngắn
kiem_tra_mang() {
    local dia_chi_target="$1"
    local dem_thanh_cong=0
    for (( i=1; i<=3; i++ )); do
        if ping -c 1 -W 2 "$dia_chi_target" >/dev/null 2>&1; then
            (( dem_thanh_cong++ ))
        fi
        sleep 0.5
    done

    if [[ $dem_thanh_cong -eq 3 ]]; then
        echo "$dia_chi_target: ONLINE"
        return 0
    elif [[ $dem_thanh_cong -eq 0 ]]; then
        echo "$dia_chi_target: OFFLINE - Báo động mất kết nối"
        return 1
    else
        echo "$dia_chi_target: INTERMITTENT - Đường truyền không ổn định"
        return 2
    fi
}
kiem_tra_mang "8.8.8.8"

Giám sát Tài nguyên RAM & CPU Top Process

#!/usr/bin/env bash
thong_ke_tai_nguyen() {
    local mem_info
    mem_info=$(free -m | awk '/^Mem:/ {printf "Used: %sMB / Free: %sMB (%.1f%%)\n", $3, $7, ($3/$2)*100}')
    echo "=== Tình trạng Bộ nhớ ==="
    echo "$mem_info"
    echo ""

    echo "=== 5 Tiến trình chiếm RAM cao nhất ==="
    ps aux --sort=-%mem | awk 'NR==1 || NR>1 && NR<=6 {printf "%-15s %6sMB  %s\n", $11, $6/1024, $1}' | column -t
}
thong_ke_tai_nguyen

Sao lưu Nhật ký MySQL (Binary Logs)

#!/usr/bin/env bash
SAO_LU_BINLOG() {
    local dir_goc="/var/lib/mysql"
    local file_hien_tai
    file_hien_tai=$(mysql -u root -e "SHOW MASTER STATUS\G" 2>/dev/null | grep -E "^ *File:" | awk '{print $2}')
    
    if [[ -z "$file_hien_tai" ]]; then
        echo "Lỗi: Không xác định được tệp binlog hiện tại."
        exit 1
    fi

    local ngay_hien_tai
    ngay_hien_tai=$(date +%F)
    local ten_goi_nen="${ngay_hien_tai}_mysql_binlog.tar.gz"

    # Xóa bộ nhớ cache log để ghi sang tệp mới
    mysql -u root -e "FLUSH LOGS;" 2>/dev/null
    
    # Đóng gói
    tar czf "$ten_goi_nen" "$dir_goc/$file_hien_tai" 2>/dev/null
    
    # Tạo mã kiểm tra tính toàn vẹn
    md5sum "$ten_goi_nen" > "${ngay_hien_tai}_checksum.md5"
    
    echo "Đã đóng gói xong: $ten_goi_nen"
    # Lệnh SCP/RSYNC đến máy dự phòng có thể thêm vào đây
}
SAO_LU_BINLOG

Thẻ: bash-shell-scripting linux-system-administration expect-automation linux-monitoring process-management

Đăng vào ngày 10 tháng 10 lúc 17:16