Function trong Bash shell là một khối lệnh được đặt tên, dùng để gom nhóm các câu lệnh lại và tái sử dụng nhiều lần trong script thay vì phải viết lặp đi lặp lại. Trong bài viết này, mình sẽ hướng dẫn cách gọi và truyền tham số cho function trong Bash, đồng thời sử dụng các tham số đặc biệt để viết script linh hoạt và dễ bảo trì.
Những điểm chính
- Định nghĩa Function trong Bash shell: Hiểu rõ khái niệm function là một khối lệnh được đặt tên, giúp gom nhóm và tái sử dụng mã lệnh.
- Lợi ích: Nắm rõ các lợi ích chính khi sử dụng Function trong Bash shell như tối ưu hóa script, tuân thủ nguyên tắc DRY và đơn giản hóa việc bảo trì hệ thống lâu dài.
- Cú pháp khai báo: Thành thạo các cách định nghĩa hàm khác nhau, từ chuẩn POSIX đến cách viết rút gọn trên một dòng lệnh.
- Cách gọi và quản lý: Biết cách thực thi hàm, kiểm tra mã nguồn của hàm và xóa hàm khỏi bộ nhớ shell một cách chuyên nghiệp.
- Phạm vi biến: Hiểu sâu về biến toàn cục và cách dùng từ khóa
localđể tránh xung đột dữ liệu khi viết các script phức tạp. - Xử lý tham số: Làm chủ kỹ thuật truyền tham số và sử dụng các biến đặc biệt ($0, $#, $?, $@) để tăng tính linh hoạt cho hàm.
- Giá trị trả về: Phân biệt được cách trả về mã trạng thái thoát và cách hứng dữ liệu thực tế thông qua stdout và command substitution.
- Ứng dụng thực tế: Áp dụng hàm vào các kịch bản DevOps thực tế.
- Giải đáp thắc mắc (FAQ): Có được câu trả lời cho các câu hỏi thực tế về khi nào nên tách logic thành
function, cách khai báo và cơ chế trả về dữ liệu trong Bash.
Function trong Bash shell là gì?
Function trong Bash shell là một tập hợp các lệnh được nhóm lại dưới một tên cụ thể, cho phép tái sử dụng mã lệnh nhiều lần trong script. Khi bạn gọi tên hàm, khối lệnh đó sẽ được thực thi.
Về bản chất, hàm trong Bash tương tự như:
- Procedure/Subroutine: Trong các ngôn ngữ lập trình thủ tục.
- Method: Trong các ngôn ngữ hướng đối tượng (nhưng đơn giản hơn).

Những lợi ích khi sử dụng function
Việc sử dụng function mang lại các lợi ích cốt lõi:
- Tối ưu và chuẩn hóa mã nguồn: Việc sử dụng hàm giúp loại bỏ sự lặp lại của các đoạn mã xử lý giống nhau, đồng thời đảm bảo tuân thủ nghiêm ngặt nguyên tắc DRY (Don’t Repeat Yourself). Nhờ vậy, thay vì phải sao chép và dán cùng một đoạn logic ở nhiều nơi, bạn chỉ cần định nghĩa logic đó một lần duy nhất trong hàm và gọi lại khi nào cần thiết.
- Dễ bảo trì và cập nhật: Khi cần thay đổi logic, bạn chỉ cần chỉnh sửa tại một nơi duy nhất (trong định nghĩa hàm) thay vì phải tìm và sửa hàng chục vị trí trong script.
- Cải thiện tính đọc hiểu: Việc chia nhỏ script thành các hàm có tên gọi gợi nhớ giúp người khác (hoặc chính bạn sau này) dễ dàng hiểu được mục đích của từng khối lệnh.
- Quản lý phạm vi biến: Hàm cho phép sử dụng biến cục bộ, giúp tránh xung đột dữ liệu với các phần khác của script.
- Tối ưu hóa cấu trúc: Giúp chia nhỏ các bài toán phức tạp thành các module con đơn giản, dễ kiểm soát và gỡ lỗi.

Cú pháp khai báo function trong Bash shell
Trong Bash, có hai cách chính để định nghĩa một hàm và một cách viết rút gọn như sau:
Cách 1: Sử dụng dấu ngoặc đơn ()
Dùng dấu ngoặc đơn là cách khai báo phổ biến và gọn gàng hơn, thường được các lập trình viên ưu tiên sử dụng. Đây là cách viết tuân thủ chuẩn POSIX, đảm bảo tính di động cao nhất giữa các loại shell khác nhau. Dưới đây là cú pháp lệnh:
ten_ham() {
# Các câu lệnh cần thực thi
echo "Đây là hàm được khai báo bằng dấu ngoặc đơn"
}
Cách 2: Sử dụng từ khóa function
Đây là cách khai báo tường minh, sử dụng từ khóa function trước tên hàm với cú pháp thực hiện như sau:
function ten_ham {
# Các câu lệnh cần thực thi
echo "Đây là hàm được khai báo bằng từ khóa function"
}
Lưu ý
Cách này rõ ràng nhưng không tuân thủ hoàn toàn chuẩn POSIX (có thể không chạy trên một số shell cũ hoặc khác Bash).
Cách 3: Viết hàm trên một dòng
Cách này thích hợp cho các hàm ngắn gọn dùng trong terminal. Lưu ý quan trọng: Bạn bắt buộc phải có dấu chấm phẩy ; sau lệnh cuối cùng trước khi đóng ngoặc nhọn.
Ví dụ:
in_ngay() { date; }Cách gọi và quản lý hàm trong Bash
Cách thực thi hàm trong Script và Terminal
Để gọi hàm, bạn chỉ cần gõ tên hàm giống như một câu lệnh thông thường (không cần dấu ngoặc đơn () phía sau).
Nguyên tắc: Hàm phải được định nghĩa/khai báo trước khi được gọi trong script.
Ví dụ:
# Định nghĩa
xin_chao() { echo "Hello World"; }
# Gọi hàm
xin_chaoKiểm tra và xem nội dung hàm
Nếu bạn muốn kiểm tra xem một hàm đã được định nghĩa chưa hoặc xem nội dung của hàm trong terminal thì hãy gõ các lệnh sau:
declare -F: Hiển thị danh sách tên các hàm đã định nghĩa.declare -f ten_ham: Hiển thị toàn bộ mã nguồn của hàm đó.
Cách xóa hàm
Để xóa một hàm khỏi bộ nhớ của shell hiện tại, bạn sử dụng lệnh:
unset ten_ham
Lưu ý
Nếu trùng tên với biến thì bạn dùng lệnh unset -f ten_ham để chỉ định xóa hàm.
Phạm vi biến trong Bash Function
Biến toàn cục
Theo mặc định, tất cả biến trong Bash đều là toàn cục, ngay cả khi chúng được khai báo bên trong hàm. Điều này có nghĩa là biến có thể được truy cập và thay đổi giá trị từ bất kỳ đâu trong script. Tuy nhiên biến toàn cục dễ gây ra lỗi “side effect” không mong muốn nếu các hàm khác nhau vô tình thay đổi cùng một biến.
Biến cục bộ
Để giới hạn phạm vi biến chỉ tồn tại trong hàm, bạn hãy sử dụng từ khóa local. Điều này sẽ giúp tránh xung đột tên biến (Shadowing/Overriding) với biến toàn cục hoặc biến của hàm khác.
Lời khuyên: Bạn nên luôn sử dụng local cho các biến tạm trong hàm.
Ví dụ:
name="Global Name"
test_scope() {
local name="Local Name"
echo "Trong hàm: $name"
}
test_scope
echo "Ngoài hàm: $name"
# Kết quả:
# Trong hàm: Local Name
# Ngoài hàm: Global NameCách xử lý tham số trong hàm
Khác với các ngôn ngữ lập trình như Python hay C++, trong Bash, bạn không khai báo tham số bên trong dấu ngoặc (). Thay vào đó, tham số được truyền vào khi gọi hàm và được truy xuất bên trong hàm thông qua các biến vị trí.
1. Cách truyền và nhận tham số
- Truyền tham số: Viết các giá trị ngay sau tên hàm, cách nhau bởi khoảng trắng.
- Nhận tham số: Sử dụng
$1cho tham số đầu tiên,$2cho tham số thứ hai,…
Ví dụ minh họa:

2. Các tham số đặc biệt
Bên cạnh các biến vị trí $1, $2…, Bash cung cấp các biến đặc biệt giúp bạn kiểm soát luồng xử lý của hàm tốt hơn:
| Tham số | Ý nghĩa | Ví dụ sử dụng |
|---|---|---|
$0 | Tên của file script đang chạy. | echo "Đang chạy script: $0" |
$# | Tổng số lượng đối số đã được truyền vào hàm. | if [ $# -eq 0 ]; then echo "Thiếu tham số"; fi |
$? | Trạng thái thoát của lệnh hoặc hàm vừa thực thi. | 0 là thành công, khác 0 là lỗi. |
$@ | Đại diện cho tất cả các tham số truyền vào. | for arg in "$@"; do ... done |
Để tra cứu nhanh các thông tin kỹ thuật về hàm trong terminal, bạn có thể sử dụng lệnh:
help functionGiá trị trả về từ hàm
Khác với các ngôn ngữ lập trình như Python hay JavaScript, Bash xử lý giá trị trả về theo cách riêng biệt.
Trả về trạng thái thoát
Hàm sử dụng từ khóa return để trả về trạng thái thành công hoặc thất bại. Trong đó, giá trị trả về sẽ là số nguyên từ 0 đến 255.
- 0: Thành công.
- Khác 0 (1-255): Thất bại/Lỗi.
Bạn có thể kiểm tra giá trị này ngay sau khi gọi hàm bằng biến $?.
Trả về dữ liệu thực tế
Bash không hỗ trợ return “chuỗi dữ liệu”. Để lấy dữ liệu đầu ra từ hàm, bạn có 2 giải pháp:
- Gán vào biến toàn cục: Cách này không khuyến khích vì khó debug.
- Sử dụng echo và Command Substitution: Cách này sẽ in kết quả ra stdout và hứng lấy giá trị khi gọi hàm.
Ví dụ:
lay_ngay_gio() {
local now=$(date +%F)
echo "$now" # In ra stdout thay vì return
}
# Hứng dữ liệu vào biến
current_date=$(lay_ngay_gio)
echo "Hôm nay là: $current_date"Ví dụ ứng dụng của function trong bash shell thực tế
Script kiểm tra tài nguyên hệ thống
Sau đây là một script ví dụ để tự động kiểm tra dung lượng ổ đĩa và cảnh báo.
check_disk_usage() {
local threshold=90
local usage=$(df / | grep / | awk '{ print $5 }' | sed 's/%//g')
if [ "$usage" -gt "$threshold" ]; then
echo "CẢNH BÁO: Ổ cứng đầy ($usage%)"
return 1
else
echo "OK: Ổ cứng ổn định ($usage%)"
return 0
fi
}Tổ chức Script theo dạng Module
Bạn có thể tách các hàm ra một file riêng (ví dụ utils.sh) và import vào script chính để thu gọn code thông qua lệnh source hoặc dấu chấm .:
# Trong file main.sh
source ./utils.sh
# Hoặc
. ./utils.sh
# Sau đó gọi hàm từ utils.sh bình thườngGhi đè lệnh hệ thống
Bạn có thể tạo wrapper để thêm chức năng cho lệnh chuẩn. Ví dụ: Tự động ls sau khi cd.
cd() {
builtin cd "$@" && ls # Dùng builtin để gọi lệnh cd gốc
}Câu hỏi thường gặp
Khi nào nên tách logic thành function thay vì viết thẳng trong script?
Bạn nên tách thành function khi một đoạn logic được dùng từ 2 lần trở lên hoặc khi đoạn xử lý đủ dài và khó hiểu khiến cho phần main của script trở nên rối. Ngoài ra, bạn nên tạo function cho từng nhiệm vụ rõ ràng để dễ test độc lập, dễ debug và có thể tái sử dụng ở script khác.
Nên dùng kiểu khai báo function name {} hay name() {}?
Cả hai đều hợp lệ trong Bash, nhưng name() {} thường được khuyến nghị vì ngắn gọn, phổ biến và tương thích tốt hơn với nhiều shell khác.
Có nên để function trả về dữ liệu bằng return trong Bash?
return trong Bash chỉ trả về mã trạng thái từ 0–255 chứ không phải giá trị kiểu chuỗi hay số như trong nhiều ngôn ngữ khác.
Để trả dữ liệu, thông thường function sẽ in ra stdout rồi bên ngoài bắt lại bằng command substitution result="$(my_func …)", còn return chỉ dùng để báo thành công/thất bại hoặc các mã lỗi đặc biệt.
Function trong Bash giúp gom nhóm các lệnh thành khối logic có tên, tái sử dụng nhiều lần, từ đó loại bỏ lặp mã và tuân thủ nguyên tắc DRY trong script. Khi kết hợp function với cơ chế truyền tham số, bạn có thể xây dựng script linh hoạt, dễ kiểm soát lỗi và rất thuận tiện cho việc mở rộng, bảo trì về sau.




