This programming guide is only available in Vietnamese. Switch to Vietnamese to read the full article.

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 =.

variables.sh
#!/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:

quoting.sh
#!/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):

parameter_expansion.sh
#!/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)

arrays.sh
#!/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)

arithmetic.sh
#!/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

Related Articles

Bài viết liên quan trong series

Lesson 3: Control Flow: Conditionals, Loops & Exit Codes Bài 3: Luồng Điều Khiển — Điều Kiện, Vòng Lặp & Exit Codes Lesson 1: Terminal Basics: Filesystem, Commands & Permissions Bài 1: Làm Quen Terminal — Filesystem, Lệnh Cơ Bản & Permissions Back to Bash Series Overview Quay lại Lộ trình Bash Series