This programming guide is only available in Vietnamese. Switch to Vietnamese to read the full article.
Đến bài này, bạn đã biết cách sử dụng biến, điều kiện và vòng lặp. Giờ là lúc tổ chức code thành functions có thể tái sử dụng và viết script hoàn chỉnh với cấu trúc chuyên nghiệp. Đây là bước chuyển từ "người dùng terminal" sang "shell script developer".
1. Shebang & Cách Chạy Script
Dòng đầu tiên của mọi shell script nên là shebang — cho hệ điều hành biết dùng interpreter nào để chạy file:
#!/bin/bash
# Shebang trỏ trực tiếp đến /bin/bash
#!/usr/bin/env bash
# Tốt hơn: dùng env để tìm bash trong $PATH (portable hơn)
# Trên một số hệ thống, bash nằm ở /usr/local/bin/bash
# === 3 cách chạy script ===
# Cách 1: ./script.sh (cần chmod +x trước)
chmod +x myscript.sh
./myscript.sh # chạy trong subprocess MỚI
# Cách 2: bash script.sh (không cần chmod +x)
bash myscript.sh # chạy trong subprocess mới
# Cách 3: source script.sh (chạy trong shell HIỆN TẠI)
source myscript.sh # tương đương: . myscript.sh
# ⚠️ Biến, function được define trong script sẽ TỒN TẠI trong shell hiện tại!
# === Sự khác biệt quan trọng ===
# Tạo file test_source.sh:
# #!/bin/bash
# MY_VAR="hello from script"
# cd /tmp
# Chạy bằng ./test_source.sh:
# → MY_VAR KHÔNG tồn tại trong shell cha
# → Thư mục hiện tại KHÔNG thay đổi
# Chạy bằng source test_source.sh:
# → MY_VAR CÓ tồn tại trong shell hiện tại
# → Thư mục hiện tại THAY ĐỔI sang /tmp
2. Tạo Functions
#!/bin/bash
# === Cú pháp khai báo function ===
# Cách 1 (khuyên dùng):
greet() {
echo "Xin chào, $1!" # $1 = argument đầu tiên
}
# Cách 2 (có keyword function):
function goodbye {
echo "Tạm biệt, $1!"
}
# Gọi function (KHÔNG có dấu ngoặc đơn)
greet "Quang" # Xin chào, Quang!
goodbye "World" # Tạm biệt, World!
# === Arguments trong function ===
show_info() {
echo "Tên script: $0" # tên file script (không phải tên function)
echo "Argument 1: $1"
echo "Argument 2: $2"
echo "Tất cả args: $@"
echo "Số lượng args: $#"
}
show_info "Alice" "Bob" "Charlie"
# === local keyword — biến cục bộ trong function ===
my_func() {
local local_var="Tôi chỉ tồn tại trong function"
global_var="Tôi tồn tại ở mọi nơi"
echo "$local_var"
}
my_func
echo "$global_var" # Tôi tồn tại ở mọi nơi
echo "$local_var" # (rỗng — không tồn tại ngoài function)
# === Function thực tế: validate input ===
is_number() {
local value="$1"
if [[ "$value" =~ ^[0-9]+$ ]]; then
return 0 # true (success)
else
return 1 # false (failure)
fi
}
if is_number "42"; then
echo "42 là số hợp lệ"
fi
if ! is_number "abc"; then
echo "abc không phải số"
fi
3. Return vs Exit
#!/bin/bash
# === return N — thoát FUNCTION, trả về exit code (0-255) ===
check_file() {
if [[ -f "$1" ]]; then
return 0 # thành công
else
return 1 # thất bại
fi
}
check_file "/etc/passwd"
echo "Exit code: $?" # 0
# === exit N — thoát TOÀN BỘ SCRIPT ===
# Nếu bạn dùng exit trong function → thoát luôn script!
# Chỉ dùng exit ở main script level hoặc khi muốn dừng hoàn toàn.
# === Trả về STRING từ function ===
# return chỉ trả số 0-255, KHÔNG trả string!
# Dùng command substitution để "trả về" string:
get_timestamp() {
date +"%Y-%m-%d %H:%M:%S" # echo ra stdout
}
# Bắt output bằng $()
current_time=$(get_timestamp)
echo "Thời gian: $current_time"
# === Pattern: function trả về giá trị qua stdout ===
to_uppercase() {
echo "$1" | tr '[:lower:]' '[:upper:]'
}
result=$(to_uppercase "hello bash")
echo "$result" # HELLO BASH
# === Pattern: function set biến global (tránh dùng nếu có thể) ===
get_system_info() {
SYSTEM_OS=$(uname -s)
SYSTEM_ARCH=$(uname -m)
SYSTEM_HOSTNAME=$(hostname)
}
get_system_info
echo "OS: $SYSTEM_OS, Arch: $SYSTEM_ARCH, Host: $SYSTEM_HOSTNAME"
4. Xử lý Arguments với getopts
#!/bin/bash
# === Biến đặc biệt cho arguments ===
echo "Script name: $0"
echo "First arg: $1"
echo "Second arg: $2"
echo "All args: $@" # mỗi arg là string riêng (khuyên dùng)
echo "All args: $*" # tất cả args gộp thành 1 string
echo "Arg count: $#"
# === shift — dịch arguments sang trái ===
echo "Before shift: $1 $2 $3" # a b c
shift # bỏ $1, $2→$1, $3→$2
echo "After shift: $1 $2" # b c
# === getopts — parse short options ===
# Cú pháp: getopts "optstring" variable
# Dấu : sau option = option yêu cầu argument
usage() {
echo "Usage: $0 [-f file] [-o output] [-v] [-h]"
echo " -f FILE Input file"
echo " -o OUTPUT Output file"
echo " -v Verbose mode"
echo " -h Show help"
}
verbose=false
input_file=""
output_file=""
while getopts "f:o:vh" opt; do
case $opt in
f) input_file="$OPTARG" ;;
o) output_file="$OPTARG" ;;
v) verbose=true ;;
h) usage; exit 0 ;;
\?) echo "Invalid option: -$OPTARG" >&2; usage; exit 1 ;;
:) echo "Option -$OPTARG requires an argument" >&2; exit 1 ;;
esac
done
# Bỏ qua các options đã parse, giữ lại positional args
shift $((OPTIND - 1))
echo "Input: $input_file"
echo "Output: $output_file"
echo "Verbose: $verbose"
echo "Remaining args: $@"
# Chạy: ./arguments.sh -f data.csv -o result.txt -v extra1 extra2
# === Manual parsing cho long options ===
while [[ $# -gt 0 ]]; do
case $1 in
--file)
input_file="$2"
shift 2
;;
--output)
output_file="$2"
shift 2
;;
--verbose)
verbose=true
shift
;;
--help)
usage
exit 0
;;
--)
shift
break
;;
-*)
echo "Unknown option: $1" >&2
exit 1
;;
*)
break
;;
esac
done
5. Best Practices: Template Script Chuẩn
#!/usr/bin/env bash
#
# Script: process_data.sh
# Mô tả: Đọc file CSV, xử lý và xuất kết quả
# Tác giả: Your Name
# Ngày: 2026-06-24
#
# === Strict Mode — BẮT BUỘC cho mọi script nghiêm túc ===
set -euo pipefail
# -e: thoát ngay khi lệnh lỗi (exit code != 0)
# -u: báo lỗi khi dùng biến chưa khai báo
# -o pipefail: pipeline thất bại nếu BẤT KỲ lệnh nào trong pipe lỗi
# === Constants ===
readonly SCRIPT_NAME=$(basename "$0")
readonly SCRIPT_DIR=$(cd "$(dirname "$0")" && pwd)
readonly LOG_FILE="/tmp/${SCRIPT_NAME}.log"
# === Logging Function ===
log() {
local level="$1"
shift
local message="$*"
local timestamp=$(date +"%Y-%m-%d %H:%M:%S")
echo "[$timestamp] [$level] $message" | tee -a "$LOG_FILE"
}
log_info() { log "INFO" "$@"; }
log_warn() { log "WARN" "$@"; }
log_error() { log "ERROR" "$@"; }
# === Cleanup Function — chạy khi script thoát (kể cả lỗi) ===
cleanup() {
local exit_code=$?
if [[ -f "${TMP_FILE:-}" ]]; then
rm -f "$TMP_FILE"
log_info "Đã dọn dẹp file tạm: $TMP_FILE"
fi
if [[ $exit_code -ne 0 ]]; then
log_error "Script thoát với mã lỗi: $exit_code"
fi
exit $exit_code
}
trap cleanup EXIT # Luôn chạy cleanup khi thoát
trap 'exit 1' INT TERM # Ctrl+C → cleanup → exit
# === Usage/Help ===
usage() {
cat <<EOF
Usage: $SCRIPT_NAME [OPTIONS] <input_file>
Đọc file CSV, xử lý dữ liệu và xuất kết quả.
Options:
-o, --output FILE File kết quả (mặc định: stdout)
-d, --delimiter C Ký tự phân tách (mặc định: ,)
-v, --verbose Hiển thị chi tiết
-h, --help Hiện hướng dẫn này
Examples:
$SCRIPT_NAME data.csv
$SCRIPT_NAME -o result.txt -d ";" data.csv
EOF
}
# === Parse Arguments ===
output_file=""
delimiter=","
verbose=false
while [[ $# -gt 0 ]]; do
case $1 in
-o|--output) output_file="$2"; shift 2 ;;
-d|--delimiter) delimiter="$2"; shift 2 ;;
-v|--verbose) verbose=true; shift ;;
-h|--help) usage; exit 0 ;;
-*) log_error "Option không hợp lệ: $1"; usage; exit 1 ;;
*) break ;;
esac
done
# === Validate Input ===
input_file="${1:-}"
if [[ -z "$input_file" ]]; then
log_error "Thiếu file đầu vào"
usage
exit 1
fi
if [[ ! -f "$input_file" ]]; then
log_error "File không tồn tại: $input_file"
exit 1
fi
# === Main Logic ===
main() {
log_info "Bắt đầu xử lý: $input_file"
TMP_FILE=$(mktemp)
local line_count=0
while IFS="$delimiter" read -r col1 col2 col3; do
(( line_count++ ))
$verbose && log_info "Dòng $line_count: $col1 | $col2 | $col3"
echo "$col1,$col2,$col3" >> "$TMP_FILE"
done < "$input_file"
# Xuất kết quả
if [[ -n "$output_file" ]]; then
cp "$TMP_FILE" "$output_file"
log_info "Kết quả đã ghi vào: $output_file"
else
cat "$TMP_FILE"
fi
log_info "Hoàn tất! Đã xử lý $line_count dòng."
}
main
6. Câu hỏi trắc nghiệm ôn tập
Trắc nghiệm 1: source vs ./
Script setup.sh chứa export API_KEY="abc123". Sau khi chạy
./setup.sh, bạn gõ echo $API_KEY trong terminal. Kết quả là gì?
Trắc nghiệm 2: return trong function
return trong function Bash có thể trả về giá trị gì?
Trắc nghiệm 3: set -euo pipefail
Tác dụng của set -u là gì?
Tải file code thực hành minh họa bài học
File script mẫu hoàn chỉnh với functions, argument parsing, error handling và logging:
Tải về functions_scripts.sh
Comments
Bình luận