Lập trình · Ngôn ngữ lập trình
Học viết script Bash cơ bản cho người mới bắt đầu
Trả lời nhanh
Bash là shell dòng lệnh Unix và cũng là ngôn ngữ script, được dùng trên máy chủ Linux, terminal macOS và pipeline CI. Một script Bash là tệp văn bản chứa các lệnh shell, kết hợp với biến, điều kiện, vòng lặp, hàm và pipe. Trang này hướng dẫn viết và chạy script, đặt dấu nháy cho biến an toàn, điều khiển luồng, dùng mảng, chuyển hướng đầu ra và xử lý lỗi bằng set -euo pipefail.
Mở Tính chmodBash là gì và dùng để làm gì
Bash (Bourne Again SHell) là shell dòng lệnh và ngôn ngữ script của Dự án GNU. Bash đọc các lệnh bạn gõ, chạy chương trình và nối chúng bằng pipe và chuyển hướng. Script Bash là tệp văn bản chứa chính những lệnh đó, nên mọi việc bạn làm tay trong terminal đều có thể tự động hóa.
Bash là shell tương tác mặc định trên hầu hết bản phân phối Linux và có trên macOS (hiện mặc định dùng zsh nhưng vẫn kèm Bash), cũng như trên Windows qua WSL hoặc Git Bash. Lập trình viên, quản trị hệ thống và kỹ sư DevOps dùng Bash cho script build, triển khai, job CI, bảo trì máy chủ và xử lý dữ liệu nhanh với các công cụ như grep, sed, awk và sort.
Viết và chạy Hello, world
Script bắt đầu bằng dòng shebang cho hệ thống biết dùng trình thông dịch nào. Cấp quyền thực thi cho tệp bằng chmod, rồi chạy nó.
#!/usr/bin/env bash
# hello.sh
echo "Hello, world!"chmod +x hello.sh
./hello.sh
# Hello, world!
bash hello.sh # also works without chmod
bash --version # check which version you have#!/usr/bin/env bash tìm Bash trong PATH, di động hơn so với việc ghi cứng /bin/bash. Nếu script chỉ dùng tính năng POSIX, có thể dùng #!/bin/sh, nhưng khi đó không được dùng cú pháp chỉ có ở Bash như [[ ]] và mảng.
Biến, dấu nháy và khai triển
Gán bằng name=value và đọc bằng $name hoặc ${name}. Mọi biến đều là chuỗi; Bash chỉ làm số học nguyên bên trong $(( )). Hãy đặt khai triển trong nháy kép để khoảng trắng và ký tự đặc biệt được giữ nguyên.
name="Ada" # no spaces around =
count=3
echo "Hello, $name" # Hello, Ada
echo "Next: $((count + 1))" # Next: 4 (integer arithmetic)
today=$(date +%Y-%m-%d) # command substitution
echo "Today is $today"
file="my report.txt"
echo "${file%.txt}.pdf" # my report.pdf (strip suffix)
echo "${#name}" # 3 (string length)
echo "${missing:-default}" # default (fallback value)| Cú pháp | Ý nghĩa |
|---|---|
"$var" | giá trị của biến, giữ thành một từ |
'text' | văn bản nguyên văn, không khai triển gì cả |
$(command) | đầu ra của một lệnh |
$((a + b)) | số học nguyên |
$1, $2, $@, $# | đối số của script hoặc hàm, tất cả đối số, số lượng đối số |
$? | mã thoát của lệnh cuối cùng (0 nghĩa là thành công) |
Điều kiện và case
if chạy một lệnh và kiểm tra mã thoát của nó. Lệnh kiểm tra [[ ]] so sánh chuỗi và kiểm tra tệp; (( )) so sánh số. case so khớp một giá trị với các mẫu glob.
#!/usr/bin/env bash
file="${1:-notes.txt}"
if [[ -f "$file" ]]; then
echo "$file exists"
elif [[ -d "$file" ]]; then
echo "$file is a directory"
else
echo "$file not found"
fi
n=7
if (( n % 2 == 1 )); then echo "$n is odd"; fi
case "$file" in
*.txt) echo "text file" ;;
*.sh) echo "shell script" ;;
*) echo "something else" ;;
esac| Kiểm tra | Đúng khi |
|---|---|
[[ -f path ]] | đường dẫn là tệp thông thường |
[[ -d path ]] | đường dẫn là thư mục |
[[ -z "$s" ]] | chuỗi rỗng |
[[ "$a" == "$b" ]] | hai chuỗi bằng nhau |
(( a > b )) | số a lớn hơn số b |
[[ $a -lt $b ]] | số a nhỏ hơn số b (kiểu cũ) |
Vòng lặp
Bash có for trên danh sách các từ, for (( )) kiểu C, while và until. Đọc tệp từng dòng dùng while read.
for fruit in apple banana cherry; do
echo "fruit: $fruit"
done
for (( i = 1; i <= 3; i++ )); do
echo "i = $i"
done
for f in *.log; do
[[ -e "$f" ]] || continue # skip if no .log files match
echo "log file: $f"
done
while IFS= read -r line; do
echo "line: $line"
done < input.txtIFS= read -r đọc mỗi dòng đúng như đã viết: IFS= giữ khoảng trắng đầu dòng và -r giữ dấu gạch chéo ngược.
Hàm
Định nghĩa hàm bằng name() { ... } và gọi như một lệnh. Đối số đến dưới dạng $1, $2, v.v. Hàm trả về mã thoát (0 đến 255) bằng return; để trả dữ liệu, hãy in ra và bắt đầu ra bằng $( ).
greet() {
local name="${1:-world}" # local variable, default value
echo "Hello, $name!"
}
is_even() {
(( $1 % 2 == 0 )) # exit status 0 means true
}
greet # Hello, world!
greet "Ann" # Hello, Ann!
msg=$(greet "Bo") # capture output
echo "Got: $msg" # Got: Hello, Bo!
if is_even 4; then echo "4 is even"; fiMảng và mảng kết hợp
Mảng theo chỉ mục chứa danh sách chuỗi. Mảng kết hợp ánh xạ khóa chuỗi sang giá trị và cần Bash 4 trở lên, nên không có trong Bash 3.2 cũ đi kèm macOS.
langs=("bash" "python" "go")
langs+=("rust")
echo "${langs[0]}" # bash
echo "${#langs[@]}" # 4
for l in "${langs[@]}"; do echo "- $l"; done
# associative array (Bash 4 or later)
declare -A port
port[http]=80
port[https]=443
echo "${port[https]}" # 443
for key in "${!port[@]}"; do
echo "$key -> ${port[$key]}"
doneLuôn viết "${array[@]}" có nháy khi lặp qua các phần tử; cách này giữ mỗi phần tử là một từ ngay cả khi nó chứa khoảng trắng.
Pipe và chuyển hướng
Điểm đặc trưng của Bash là ghép các chương trình nhỏ. Pipe | gửi đầu ra của một lệnh sang đầu vào của lệnh kế tiếp. Chuyển hướng gửi đầu ra vào tệp hoặc đọc đầu vào từ tệp. Mô tả tệp 1 là đầu ra chuẩn và 2 là lỗi chuẩn.
ls -l > files.txt # write stdout to a file (overwrite)
echo "more" >> files.txt # append
grep error app.log 2> /dev/null # discard error messages
make > build.log 2>&1 # stdout and stderr to the same file
# count the 3 most common words in a file
tr -cs '[:alpha:]' '\n' < book.txt | tr '[:upper:]' '[:lower:]' |
sort | uniq -c | sort -rn | head -n 3Xử lý lỗi và script an toàn
Mặc định Bash vẫn tiếp tục sau khi một lệnh thất bại. Nhiều script mở đầu bằng set -euo pipefail: -e thoát khi có lỗi không được xử lý, -u coi biến chưa đặt là lỗi, và -o pipefail khiến pipeline thất bại nếu bất kỳ lệnh nào trong đó thất bại. trap chạy mã dọn dẹp khi script thoát.
#!/usr/bin/env bash
set -euo pipefail # stop on errors, unset vars, pipe failures
tmp=$(mktemp)
trap 'rm -f "$tmp"' EXIT # clean up when the script exits
if ! grep -q "ready" status.txt; then
echo "status is not ready" >&2 # errors go to stderr
exit 1
fi
cp config.txt "$tmp" || { echo "copy failed" >&2; exit 2; }
echo "ok"Dùng || để xử lý lỗi trên một dòng và && để chỉ chạy lệnh khi lệnh trước thành công. set -e có ngoại lệ (ví dụ lệnh trong điều kiện if không kích hoạt nó), nên hãy kiểm tra tường minh các lệnh quan trọng.
Một script hoàn chỉnh nhỏ: sao lưu theo ngày
Script này nén một thư mục thành tệp .tar.gz có dấu thời gian và chỉ giữ năm bản nén mới nhất. Nó dùng đối số, giá trị mặc định, kiểm tra, thay thế lệnh và pipe.
#!/usr/bin/env bash
# backup.sh: copy a directory into a dated .tar.gz archive
set -euo pipefail
usage() { echo "usage: $0 SOURCE_DIR [DEST_DIR]" >&2; exit 1; }
[[ $# -ge 1 ]] || usage
src="$1"
dest="${2:-$HOME/backups}"
[[ -d "$src" ]] || { echo "not a directory: $src" >&2; exit 1; }
mkdir -p "$dest"
stamp=$(date +%Y%m%d-%H%M%S)
archive="$dest/$(basename "$src")-$stamp.tar.gz"
tar -czf "$archive" -C "$(dirname "$src")" "$(basename "$src")"
size=$(du -h "$archive" | cut -f1)
echo "Created $archive ($size)"
# keep only the 5 newest archives for this source
ls -1t "$dest"/"$(basename "$src")"-*.tar.gz | tail -n +6 | while read -r old; do
rm -f -- "$old"
echo "Removed $old"
done./backup.sh ~/projects/site
# Created /home/ann/backups/site-20261031-093000.tar.gz (2.1M)Học tiếp ở đâu
- GNU Bash Reference Manual, tài liệu chính thức.
man bashvàhelp(ví dụhelp read) ngay trong terminal.- ShellCheck, công cụ phân tích tĩnh giải thích các lỗi script phổ biến.
- Đặc tả shell POSIX nếu bạn cần script chạy được cả trong
sh,dashhoặc BusyBox.
Câu hỏi thường gặp
Bash và sh khác nhau thế nào?
sh là chuẩn shell POSIX, được triển khai bởi các chương trình như dash. Bash tương thích với POSIX sh nhưng bổ sung các tính năng như [[ ]], mảng, (( )) và khai triển ngoặc nhọn.
Bash và zsh khác nhau thế nào?
Hai shell tương thích với hầu hết lệnh thường ngày. zsh bổ sung nhiều tính năng tương tác và là shell mặc định trên macOS; script viết với shebang bash vẫn chạy bằng Bash.
Vì sao script báo permission denied?
Tệp chưa có quyền thực thi. Hãy chạy chmod +x script.sh, hoặc khởi chạy bằng bash script.sh.
Làm sao truyền đối số cho script Bash?
Viết chúng sau tên script, như ./script.sh one two. Bên trong script chúng là $1 và $2, $@ chứa tất cả và $# là số lượng.
Có nên viết chương trình lớn bằng Bash không?
Bash phù hợp nhất để kết nối các lệnh với nhau. Khi script trở nên phức tạp, cần cấu trúc dữ liệu hoặc xử lý lỗi cẩn thận, một ngôn ngữ như Python thường dễ bảo trì hơn.