Biến (variables) là nền tảng của mọi ngôn ngữ lập trình. Trong Bash, cách khai báo và sử dụng biến có nhiều đặc thù riêng — từ quy tắc quoting nghiêm ngặt đến hệ thống parameter expansion cực kỳ mạnh mẽ. Bài này sẽ giúp bạn nắm vững tất cả.
1. Khai báo biến & Quy tắc đặt tên
Trong Bash, khai báo biến sử dụng cú pháp NAME=value. Lưu ý quan trọng:
không có khoảng trắng quanh dấu =.
#!/bin/bash
# ✅ Đúng: không có space quanh =
name="Quang"
age=25
project_dir="/home/user/projects"
# ❌ Sai: có space → Bash hiểu "name" là lệnh, "=" là argument
# name = "Quang" → bash: name: command not found
# Sử dụng biến: dùng $ hoặc ${}
echo $name # Quang
echo "Hello, ${name}!" # Hello, Quang!
# Quy ước đặt tên:
# UPPERCASE → biến môi trường, hằng số: PATH, HOME, DB_HOST
# lowercase → biến cục bộ trong script: file_count, temp_dir
readonly MAX_RETRIES=3 # hằng số (không thể thay đổi)
# MAX_RETRIES=5 # bash: MAX_RETRIES: readonly variable
# Biến môi trường vs biến cục bộ
MY_VAR="local" # chỉ có trong shell hiện tại
export DB_HOST="localhost" # biến môi trường, con process kế thừa được
env | grep DB_HOST # kiểm tra biến môi trường
unset DB_HOST # xóa biến
2. Quoting: Single vs Double vs Command Substitution
Quoting là một trong những khái niệm quan trọng nhất trong Bash. Mỗi kiểu quote có hành vi khác nhau:
#!/bin/bash
name="World"
# Single quotes: literal — KHÔNG expand biến, KHÔNG xử lý ký tự đặc biệt
echo 'Hello, $name!' # Hello, $name!
echo 'Dấu \n không xuống dòng' # Dấu \n không xuống dòng
# Double quotes: expand biến và command substitution
echo "Hello, $name!" # Hello, World!
echo "Home dir: $HOME" # Home dir: /home/user
echo "Ngày hôm nay: $(date)" # Ngày hôm nay: Tue Jun 24 ...
# Command substitution: $(command) — thay thế bằng output của lệnh
current_date=$(date +%Y-%m-%d)
file_count=$(ls | wc -l)
echo "Có $file_count files, ngày $current_date"
# Backtick cũ (tránh dùng, khó đọc khi lồng nhau):
# old_way=`date +%Y-%m-%d` # tương đương $(date +%Y-%m-%d)
# Escape ký tự đặc biệt trong double quotes
echo "Giá: \$100" # Giá: $100
echo "Dấu ngoặc: \"xin chào\"" # Dấu ngoặc: "xin chào"
3. Parameter Expansion nâng cao
Đây là tính năng cực kỳ mạnh mẽ giúp xử lý chuỗi mà không cần gọi lệnh bên ngoài (sed, awk):
#!/bin/bash
# === Giá trị mặc định ===
echo ${name:-"Guest"} # Nếu name rỗng/chưa set → dùng "Guest" (không gán)
echo ${name:="Guest"} # Nếu name rỗng/chưa set → gán "Guest" vào name
echo ${name:+"có giá trị"} # Nếu name CÓ giá trị → trả về "có giá trị"
echo ${name:?"Lỗi: biến name chưa được đặt"} # Nếu rỗng → báo lỗi và thoát
# === Độ dài chuỗi ===
str="Hello Bash"
echo ${#str} # 10 (số ký tự)
# === Cắt chuỗi (Substring) ===
echo ${str:0:5} # Hello (từ vị trí 0, lấy 5 ký tự)
echo ${str:6} # Bash (từ vị trí 6 đến hết)
# === Trim pattern ===
filepath="/home/user/documents/report.tar.gz"
echo ${filepath#*/} # home/user/documents/report.tar.gz (bỏ prefix ngắn nhất match */)
echo ${filepath##*/} # report.tar.gz (bỏ prefix dài nhất match */)
echo ${filepath%.*} # /home/user/documents/report.tar (bỏ suffix ngắn nhất match .*)
echo ${filepath%%.*} # /home/user/documents/report (bỏ suffix dài nhất match .*)
# === Tách tên file và đường dẫn ===
echo ${filepath##*/} # report.tar.gz (tên file)
echo ${filepath%/*} # /home/user/documents (thư mục cha)
# === Thay thế chuỗi ===
text="foo bar foo baz foo"
echo ${text/foo/FOO} # FOO bar foo baz foo (thay thế lần đầu)
echo ${text//foo/FOO} # FOO bar FOO baz FOO (thay thế tất cả)
echo ${text/#foo/START} # START bar foo baz foo (thay thế ở đầu)
echo ${text/%foo/END} # foo bar foo baz END (thay thế ở cuối)
# === Chuyển đổi chữ hoa/thường (Bash 4+) ===
word="hello"
echo ${word^} # Hello (viết hoa ký tự đầu)
echo ${word^^} # HELLO (viết hoa tất cả)
upper="WORLD"
echo ${upper,} # wORLD (viết thường ký tự đầu)
echo ${upper,,} # world (viết thường tất cả)
4. Arrays (Mảng)
#!/bin/bash
# === Indexed Arrays ===
fruits=("apple" "banana" "cherry" "date")
echo ${fruits[0]} # apple (phần tử đầu tiên, index bắt đầu từ 0)
echo ${fruits[2]} # cherry
echo ${fruits[@]} # apple banana cherry date (tất cả phần tử)
echo ${#fruits[@]} # 4 (số phần tử)
echo ${!fruits[@]} # 0 1 2 3 (danh sách index)
# Thêm phần tử
fruits+=("elderberry") # thêm vào cuối
fruits[10]="fig" # gán vào index bất kỳ (Bash cho phép sparse array)
# Xóa phần tử
unset fruits[1] # xóa "banana" (index 1)
echo ${fruits[@]} # apple cherry date elderberry fig
# Lặp qua mảng
for fruit in "${fruits[@]}"; do
echo "Quả: $fruit"
done
# Slice (cắt mảng)
echo ${fruits[@]:1:3} # lấy 3 phần tử từ index 1
# === Associative Arrays (Bash 4+) ===
declare -A config
config[host]="localhost"
config[port]="3306"
config[db]="myapp"
echo ${config[host]} # localhost
echo ${config[@]} # tất cả values
echo ${!config[@]} # tất cả keys: host port db
# Lặp qua associative array
for key in "${!config[@]}"; do
echo "$key = ${config[$key]}"
done
5. Arithmetic (Phép tính số học)
#!/bin/bash
# === $(( )) — Arithmetic Expansion ===
a=10
b=3
echo $(( a + b )) # 13
echo $(( a - b )) # 7
echo $(( a * b )) # 30
echo $(( a / b )) # 3 (chia nguyên, không có phần thập phân!)
echo $(( a % b )) # 1 (phần dư)
echo $(( a ** b )) # 1000 (lũy thừa: 10^3)
# Gán kết quả
result=$(( (a + b) * 2 ))
echo $result # 26
# Tăng/giảm
(( a++ )) # a = 11
(( b-- )) # b = 2
(( a += 5 )) # a = 16
# === (( )) — Arithmetic Evaluation (trả về exit code) ===
x=10
if (( x > 5 )); then
echo "$x lớn hơn 5" # 10 lớn hơn 5
fi
if (( x % 2 == 0 )); then
echo "$x là số chẵn" # 10 là số chẵn
fi
# === let — cách cũ (ít phổ biến) ===
let "c = a + b"
let "c += 10"
# === Phép tính thập phân: dùng bc (Bash không hỗ trợ float) ===
echo "scale=2; 10 / 3" | bc # 3.33
pi=$(echo "scale=10; 4*a(1)" | bc -l) # 3.1415926535
echo "Pi = $pi"
6. Câu hỏi trắc nghiệm ôn tập
Trắc nghiệm 1: Quoting
Biến name="Bash". Output của echo 'Hello $name' là gì?
Trắc nghiệm 2: Parameter Expansion
Biến file="report.tar.gz". Output của echo ${file%%.*} là gì?
Trắc nghiệm 3: Arithmetic
Output của echo $(( 10 / 3 )) là gì?
Tải file code thực hành minh họa bài học
File script tổng hợp các ví dụ về biến, quoting, parameter expansion, arrays và arithmetic:
Tải về variables_strings.sh
Bình luận