Mục tiêu chương
Sau khi hoàn thành chương này, bạn sẽ có thể:
Tạo và chạy Script
Viết và thực thi shell script với shebang #!/bin/bash, set quyền execute với chmod +x.
Biến và tham số
Khai báo và sử dụng biến, tham số vị trí ($1, $2, $@), biến đặc biệt ($?, $$, $#).
Tính toán số học
Thực hiện phép tính số nguyên bằng let, expr, bc và cú pháp $(( )).
Lệnh điều kiện
Viết câu lệnh if/then/else/fi, case/esac để xử lý logic phân nhánh.
Vòng lặp
Sử dụng vòng lặp for, while, until để lặp lại các tác vụ xử lý danh sách và điều kiện.
grep, sed, awk, cut
Kết hợp các công cụ xử lý văn bản mạnh mẽ trong script để phân tích dữ liệu.
Lý thuyết chi tiết
Các khái niệm và cú pháp Shell Script trong chương 7
Cơ bản về Shell Script
Shell script là tệp tin văn bản chứa các lệnh Bash. Có hai cách chạy: dùng bash myscript hoặc thêm shebang #!/bin/bash và cấp quyền execute. Script có thể chứa biến, hàm, vòng lặp, điều kiện.
#!/bin/bash
# Script to display system info
echo
"Hello, $USER!"
echo
"Today is: $(date)"
echo
"Hostname: $(hostname)"
$ chmod +x myscript.sh
$ ./myscript.sh
Hello, chris!
Today is: Mon Jul 27 11:20:26 AM EDT 2025
Hostname: fedora-ws
# Debug mode: hien thi tung lenh truoc khi chay
$ bash -x myscript.sh
Biến (Variables) trong Shell Script
Biến trong Bash phân biệt chữ hoa/thường. Gán giá trị: NAME=value (không có khoảng trắng). Truy xuất: $NAME hoặc ${NAME}. Lưu output lệnh: VAR=$(command).
CITY="Springfield"
PI=3.14159265
MYDATE=$(date)
MACHINE=`uname -n`
NUM_FILES=$(/bin/ls | wc -l)
$ echo "City: $CITY, Files: $NUM_FILES"
City: Springfield, Files: 42
# Parameter expansion
MYFILENAME=/home/digby/myfile.txt
FILE=${MYFILENAME##*/} # myfile.txt
DIR=${MYFILENAME%/*} # /home/digby
NAME=${FILE%.*} # myfile
EXT=${FILE##*.} # txt
Biến đặc biệt (Special Variables)
- $0 — Tên script/lệnh
- $1, $2... — Tham số dòng lệnh
- $# — Số lượng tham số
- $@ — Tất cả tham số
- $? — Exit code lệnh vừa chạy
- $$ — PID của shell hiện tại
Lệnh read — Nhập tương tác
#!/bin/bash
read -p "Enter name: " USERNAME
echo "Hello, $USERNAME!"
$ ./script.sh
Enter name: Chris
Hello, Chris!
Tính toán số học trong Script
Bash xử lý biến như chuỗi ký tự. Để tính toán số học, dùng let, expr, bc hoặc cú pháp $(( )).
BIGNUM=1024
let RESULT=$BIGNUM/16 # RESULT=64
RESULT=`expr $BIGNUM / 16` # RESULT=64
RESULT=`echo "$BIGNUM / 16" | bc` # RESULT=64
# Cu phap $(( )) voi increment
$ I=0
$ echo "After increment: $((++I))"
After increment: 1
$ echo "Before/After: $((I++)) and $I"
Before/After: 1 and 2
# Random number 0-10
$ let foo=$RANDOM%11; echo $foo
7
Câu lệnh điều kiện (if/case)
Cú pháp if trong Bash: if [ condition ]; then ... elif ...; else ...; fi. Dùng -eq, -ne, -lt, -gt cho số; -z, -n, =, != cho chuỗi; -f, -d, -e cho file.
#!/bin/bash
SCORE=$1
if
[ $SCORE -ge 90 ]; then
echo "Excellent: A"
elif
[ $SCORE -ge 70 ]; then
echo "Good: B"
else
echo "Need improvement: C"
fi
$ ./check_score.sh 85
Good: B
# Kiem tra file ton tai
if
[ -f /etc/passwd ]; then echo "File exists"; fi
#!/bin/bash
# case/esac for multiple conditions
case
$1 in
start) echo "Starting service..." ;;
stop) echo "Stopping service..." ;;
restart) echo "Restarting service..." ;;
*) echo "Usage: $0 {start|stop|restart}" ;;
esac
$ ./service.sh start
Starting service...
Vòng lặp (for, while, until)
Ba loại vòng lặp chính trong Bash: for lặp qua danh sách; while chạy khi điều kiện đúng; until chạy khi điều kiện sai.
#!/bin/bash
# for loop - lap qua danh sach
for
FRUIT in apple banana cherry; do
echo "Fruit: $FRUIT"
done
Fruit: apple
Fruit: banana
Fruit: cherry
# for loop - lap so
for
i in $(seq 1 5); do echo "Count: $i"; done
# while loop
COUNT=1
while
[ $COUNT -le 3 ]; do
echo "Count: $COUNT"
COUNT=$((COUNT+1))
done
# for loop over files
for
FILE in /etc/*.conf; do
echo "Config: $FILE"
done
Công cụ xử lý văn bản: grep, sed, awk, cut
Các công cụ này rất quan trọng trong shell script để tìm kiếm, lọc, biến đổi và phân tích dữ liệu văn bản.
grep — Tìm kiếm mẫu
$ grep "root" /etc/passwd
root:x:0:0:root:/root:/bin/bash
$ grep -i "error" /var/log/syslog
$ grep -r "TODO" /home/chris/scripts/
$ grep -c "failed" auth.log
23
sed — Stream Editor
$ sed 's/old/new/g' file.txt
$ sed -i 's/foo/bar/g' file.txt
$ sed -n '5,10p' file.txt
$ sed '/^#/d' config.txt
# Xoa dong bat dau bang #
awk — Xử lý cột
$ awk '{print $1}' /etc/passwd
$ awk -F: '{print $1,$3}' /etc/passwd
root 0
daemon 1
$ awk '{sum+=$3} END {print sum}' data.txt
cut — Cắt cột
$ cut -d: -f1 /etc/passwd
root, daemon, bin...
$ cut -d, -f2 data.csv
$ echo "hello world" | cut -c1-5
hello
Bài Lab Thực Hành
Viết Shell Script từ đơn giản đến phức tạp
Script hiển thị thông tin hệ thống
Tạo script đầu tiên với shebang, comments và biến.
#!/bin/bash
# System Information Script
HOSTNAME=$(hostname)
KERNEL=$(uname -r)
UPTIME=$(uptime -p)
USERS=$(who | wc -l)
echo "=== System Info ==="
echo "Host: $HOSTNAME"
echo "Kernel: $KERNEL"
echo "Uptime: $UPTIME"
echo "Users logged in: $USERS"
$ chmod +x sysinfo.sh && ./sysinfo.sh
=== System Info ===
Host: fedora-ws
Kernel: 6.11.4-301.fc41.x86_64
Uptime: up 2 hours, 15 minutes
Users logged in: 2
Script với tham số dòng lệnh
Tạo script sử dụng $1, $2, $# để nhận tham số.
#!/bin/bash
echo "Script: $0"
echo "First arg: $1, Second: $2"
echo "Total args: $#"
echo "All args: $@"
$ chmod 755 /home/chris/bin/greet.sh
$ greet.sh foo bar
Script: /home/chris/bin/greet.sh
First arg: foo, Second: bar
Total args: 2
All args: foo bar
Script kiểm tra file và thư mục
Dùng if/then/else để kiểm tra sự tồn tại của file.
#!/bin/bash
FILE=$1
if
[ -z "$FILE" ]; then
echo "Usage: $0 filename"; exit 1
elif
[ -f "$FILE" ]; then
echo "$FILE is a regular file"
elif
[ -d "$FILE" ]; then
echo "$FILE is a directory"
else
echo "$FILE does not exist"
fi
$ ./check_file.sh /etc/passwd
/etc/passwd is a regular file
Script backup tự động với vòng lặp
Tạo script backup nhiều thư mục bằng vòng lặp for.
#!/bin/bash
BACKUP_DIR="/tmp/backup_$(date +%Y%m%d)"
mkdir -p "$BACKUP_DIR"
for
DIR in /etc /home /var/log; do
if
[ -d "$DIR" ]; then
tar czf "$BACKUP_DIR/$(basename $DIR).tar.gz" "$DIR"
echo "Backed up: $DIR"
fi
done
echo "Backup complete: $BACKUP_DIR"
$ ./backup.sh
Backed up: /etc
Backed up: /home
Backup complete: /tmp/backup_20251027
Xử lý văn bản với grep và awk
Phân tích /etc/passwd để lạy thông tin user.
$ grep "^chris" /etc/passwd
chris:x:1000:1000:Chris Smith:/home/chris:/bin/bash
$ awk -F: '$3 >= 1000 {print $1, $6}' /etc/passwd
chris /home/chris
alice /home/alice
$ cut -d: -f1,3 /etc/passwd | grep ":[0-9]*$"
root:0
daemon:1
$ cat /etc/passwd | sed 's/\/bin\/bash/\/bin\/sh/g'
# Thay doi shell tu bash sang sh (chi hien thi)
Script kiểm tra dịch vụ và restart nếu cần
Tạo script thực tế để kiểm tra và tự động restart service.
#!/bin/bash
SERVICE="httpd"
if
systemctl is-active --quiet "$SERVICE"; then
echo "$SERVICE is running OK"
else
echo "$SERVICE is DOWN! Restarting..."
systemctl restart "$SERVICE"
if
[ $? -eq 0 ]; then
echo "$SERVICE restarted successfully"
else
echo "FAILED to restart $SERVICE!"
fi
fi
$ sudo ./check_service.sh
httpd is DOWN! Restarting...
httpd restarted successfully
Câu hỏi Ôn tập
Kiểm tra kiến thức Chapter 7
Shebang #!/bin/bash có vai trò gì trong shell script?
Shebang cho hệ điều hành biết dùng trình thông dịch nào để chạy script. #!/bin/bash cho kernel biết dùng Bash. Shebang luôn phải ở dòng đầu tiên. Không có shebang, script vẫn chạy được nhưng có thể dùng shell hiện tại thay vì bash.
Biến $? cho biết điều gì?
$? là exit code của lệnh vừa chạy xong. Giá trị 0 = thành công. Giá trị khác 0 = lỗi. Ví dụ: sau khi chạy cp file1 file2, kiểm tra if [ $? -eq 0 ]; then echo OK; fi.
Sự khác nhau giữa $@ và $* khi đặt trong dấu ngoặc kép?
"$@" mở rộng mỗi tham số thành chuỗi riêng biệt (giữ nguyên tất cả dấu cách). "$*" gộp tất cả tham số thành một chuỗi đơn. Trong for loop, nên dùng "$@" để xử lý đúng tham số có dấu cách.
Viết vòng lặp in số từ 1 đến 10 chỉ dùng while.
i=1; while [ $i -le 10 ]; do echo $i; i=$((i+1)); done
Hoặc: for i in $(seq 1 10); do echo $i; done
Lệnh grep -v làm gì? Cho ví dụ thực tế.
$ grep -v "^#" /etc/sshd_config
grep -v loại bỏ các dòng khớp với mẫu. Ví dụ trên hiển thị tất cả dòng không phải comment (không bắt đầu bằng #) trong file cấu hình SSH.
Cú pháp để gán output của lệnh vào biến là gì?
VAR=$(command) # Cu phap hien dai
VAR=`command` # Cu phap cu (backticks)
Ví dụ: HOSTNAME=$(hostname), DATE=$(date +%Y%m%d). Cú pháp $() có thể lồng nhau được, backticks thì không.
Lệnh sed để thay thế tất cả "foo" bằng "bar" trong file là gì?
$ sed -i 's/foo/bar/g' file.txt
Cờ -i sửa thẳng file (in-place). S mỡu s/pattern/replacement/g: g=global thay tất cả. Không có g sẽ chỉ thay từng xuất hiện đầu tiên trên mỗi dòng.
Debug shell script bằng cách nào?
$ bash -x myscript.sh
# Hoặc them dong nay vao script:
set -x # bat debug mode
bash -x in ra từng lệnh trước khi thực hiện (prefix bằng +). Giúp theo dõi luồng chạy và phát hiện lỗi. Còn có thể dùng echo để in giá trị biến tại các điểm kiểm tra.