Trong thế giới lập trình shell script, Bash đóng vai trò như một công cụ quyền năng, giúp bạn tự động hóa vô số tác vụ lặp đi lặp lại hàng ngày trên hệ thống. Từ việc quản lý file, sao lưu dữ liệu cho đến triển khai ứng dụng, sức mạnh của Bash là không thể phủ nhận. Tuy nhiên, để viết được những kịch bản thông minh và linh hoạt, việc nắm vững cách kiểm soát luồng chương trình là yếu tố then chốt. Đây là lúc các toán tử logic phát huy vai trò của mình. Dù vậy, nhiều người mới bắt đầu thường gặp khó khăn trong việc hiểu rõ và áp dụng chính xác các toán tử này, dẫn đến những script hoạt động không như mong muốn. Bài viết này sẽ là kim chỉ nam chi tiết, giúp bạn làm chủ các toán tử logic cơ bản trong Bash, từ đó xây dựng nền tảng vững chắc để viết những shell script hiệu quả và chuyên nghiệp hơn.
Giới thiệu về Toán tử logic trong Bash
Bash không chỉ là một giao diện dòng lệnh, mà còn là một ngôn ngữ lập trình mạnh mẽ. Sức mạnh của nó nằm ở khả năng tự động hóa các công việc quản trị hệ thống. Bạn có thể viết các kịch bản (script) để thực hiện một chuỗi lệnh một cách tự động, tiết kiệm thời gian và giảm thiểu sai sót do con người. Tuy nhiên, để một kịch bản thực sự thông minh, nó cần phải biết cách đưa ra quyết định dựa trên các điều kiện cụ thể. Đây chính là lúc toán tử logic trở thành một công cụ không thể thiếu, giúp bạn điều khiển luồng thực thi của chương trình một cách linh hoạt.
Nhiều người mới tiếp cận Bash thường bối rối không biết làm thế nào để kết hợp nhiều điều kiện, ví dụ như “làm việc A NẾU điều kiện X VÀ điều kiện Y cùng đúng” hoặc “làm việc B NẾU điều kiện X HOẶC điều kiện Y đúng”. Sự thiếu hiểu biết này làm hạn chế khả năng viết các script phức tạp, khiến họ chỉ dừng lại ở các chuỗi lệnh tuần tự đơn giản. Việc không nắm vững toán tử logic sẽ khiến script của bạn trở nên cứng nhắc và dễ gặp lỗi khi có những thay đổi nhỏ trong môi trường thực thi.
Đừng lo lắng, bài viết này sẽ giải quyết triệt để vấn đề đó. Chúng tôi sẽ đi sâu vào từng toán tử logic cốt lõi trong Bash: AND (&&), OR (||), và NOT (!). Bạn sẽ học được cách chúng hoạt động, cách kết hợp chúng với các toán tử so sánh để kiểm tra điều kiện một cách chính xác. Bài viết sẽ có cấu trúc rõ ràng, bắt đầu từ phần giới thiệu tổng quan, đi sâu vào từng loại toán tử, cung cấp các ví dụ thực hành từ đơn giản đến phức tạp, chỉ ra các lỗi thường gặp và cách khắc phục. Cuối cùng, những lời khuyên chuyên sâu sẽ giúp bạn nâng cao kỹ năng viết script của mình lên một tầm cao mới.

Các loại toán tử logic cơ bản trong Bash
Trong Bash, toán tử logic không chỉ dùng để tính toán giá trị đúng/sai như trong các ngôn ngữ lập trình truyền thống. Thay vào đó, chúng hoạt động dựa trên “trạng thái kết thúc” (exit status) của một lệnh. Theo quy ước, một lệnh kết thúc thành công sẽ trả về trạng thái 0, trong khi một lệnh thất bại sẽ trả về một giá trị khác 0. Các toán tử logic sẽ dựa vào trạng thái này để quyết định xem có nên thực thi lệnh tiếp theo hay không.
Toán tử AND (&&)
Toán tử AND, ký hiệu là &&, hoạt động theo nguyên tắc “chỉ tiếp tục khi thành công”. Khi bạn kết nối hai lệnh bằng &&, lệnh thứ hai sẽ chỉ được thực thi nếu và chỉ nếu lệnh thứ nhất kết thúc thành công (trả về exit status 0). Nếu lệnh đầu tiên thất bại, lệnh thứ hai sẽ bị bỏ qua hoàn toàn. Đây là một cách cực kỳ hữu ích để tạo ra một chuỗi các hành động phụ thuộc lẫn nhau, đảm bảo rằng bước sau chỉ được tiến hành khi bước trước đã hoàn thành tốt đẹp.
Hãy xem một ví dụ đơn giản và rất phổ biến. Giả sử bạn muốn tạo một thư mục mới và ngay lập tức di chuyển vào bên trong thư mục đó. Bạn có thể viết:
mkdir thu_muc_moi && cd thu_muc_moi
Trong trường hợp này, lệnh cd thu_muc_moi chỉ được thực thi nếu lệnh mkdir thu_muc_moi thành công. Nếu vì một lý do nào đó (ví dụ như không đủ quyền hoặc thư mục đã tồn tại), lệnh mkdir thất bại, lệnh cd sẽ không bao giờ được gọi. Điều này giúp ngăn ngừa lỗi kịch bản cố gắng truy cập vào một thư mục không tồn tại.

Toán tử OR (||)
Trái ngược với AND, toán tử OR, ký hiệu là ||, hoạt động theo nguyên tắc “chỉ hành động khi thất bại”. Khi bạn nối hai lệnh bằng ||, lệnh thứ hai sẽ chỉ được thực thi nếu lệnh thứ nhất thất bại (trả về exit status khác 0). Nếu lệnh đầu tiên thành công, lệnh thứ hai sẽ được bỏ qua. Toán tử này rất lý tưởng cho việc xử lý lỗi hoặc cung cấp một phương án dự phòng khi hành động chính không thành công.
Một ví dụ thực tế là kiểm tra kết nối mạng. Bạn có thể thử gửi một gói tin đến một máy chủ, và nếu không thành công, sẽ thông báo lỗi. Lệnh sẽ như sau:
ping -c 1 google.com || echo "Lỗi: Không thể kết nối tới máy chủ."
Ở đây, lệnh echo sẽ chỉ chạy nếu lệnh ping thất bại trong việc kết nối tới google.com. Nếu ping thành công, bạn sẽ thấy kết quả của lệnh ping và thông báo lỗi sẽ không xuất hiện. Đây là một cách ngắn gọn và hiệu quả để xây dựng các cơ chế dự phòng trong script của bạn.

Toán tử NOT (!)
Toán tử NOT, ký hiệu là !, có một vai trò khác biệt. Nó không kết nối hai lệnh với nhau, mà thay vào đó, nó đảo ngược trạng thái kết thúc của một lệnh duy nhất. Nếu một lệnh thành công (exit status 0), việc đặt ! phía trước sẽ làm cho nó bị coi là thất bại (exit status 1), và ngược lại, nếu một lệnh thất bại, ! sẽ biến nó thành thành công. Toán tử này cực kỳ hữu ích khi bạn muốn thực hiện một hành động khi một điều kiện không xảy ra.
Ví dụ, bạn muốn kiểm tra xem một file có chứa một chuỗi văn bản cụ thể hay không, và nếu không chứa, bạn mới thực hiện một hành động. Bạn có thể sử dụng lệnh grep -q (chế độ yên lặng) để tìm kiếm. Bình thường, grep sẽ trả về 0 nếu tìm thấy và 1 nếu không tìm thấy. Bằng cách sử dụng !, chúng ta có thể đảo ngược logic này:
if ! grep -q "ERROR" /var/log/syslog; then echo "Tốt! Không tìm thấy dòng lỗi nào trong file log."; fi
Trong ví dụ này, khối lệnh echo sẽ được thực thi nếu grep -q "ERROR" ... thất bại, tức là không tìm thấy chuỗi “ERROR”. Toán tử ! đã giúp chúng ta diễn đạt điều kiện “nếu không tìm thấy” một cách rõ ràng và trực tiếp.
Sử dụng toán tử so sánh để kiểm tra điều kiện trong Bash
Để các toán tử logic phát huy hết sức mạnh, chúng cần được kết hợp với các biểu thức điều kiện. Trong Bash, bạn không thể chỉ viết if a > b. Thay vào đó, bạn phải sử dụng các toán tử so sánh đặc biệt bên trong cấu trúc test (hoặc dấu ngoặc vuông [ ... ]) và tốt hơn hết là cấu trúc [[ ... ]] hiện đại và an toàn hơn. Việc hiểu rõ các toán tử so sánh này là nền tảng để xây dựng logic phức tạp cho script của bạn.
Các toán tử so sánh phổ biến
Bash cung cấp một bộ toán tử so sánh phong phú, được chia thành các nhóm chính để xử lý chuỗi, số nguyên và các thuộc tính của file. Việc sử dụng đúng toán tử cho đúng loại dữ liệu là rất quan trọng để tránh các lỗi không mong muốn.
Đối với so sánh chuỗi:
= hoặc ==: So sánh hai chuỗi có bằng nhau không (khuyến khích dùng == trong [[ ... ]] cho dễ đọc).
!=: So sánh hai chuỗi có khác nhau không.
<: So sánh chuỗi nào nhỏ hơn theo thứ tự từ điển.
>: So sánh chuỗi nào lớn hơn theo thứ tự từ điển.
-z string: Kiểm tra xem chuỗi có rỗng không (độ dài bằng 0).
-n string: Kiểm tra xem chuỗi có khác rỗng không (độ dài khác 0).
Đối với so sánh số nguyên:
-eq: Bằng nhau (equal).
-ne: Không bằng nhau (not equal).
-lt: Nhỏ hơn (less than).
-le: Nhỏ hơn hoặc bằng (less than or equal).
-gt: Lớn hơn (greater than).
-ge: Lớn hơn hoặc bằng (greater than or equal).
Đối với kiểm tra thuộc tính file:
-e file: Kiểm tra xem file có tồn tại không.
-f file: Kiểm tra xem có phải là một file thông thường không.
-d file: Kiểm tra xem có phải là một thư mục không.
-r file: Kiểm tra xem file có quyền đọc không.
-w file: Kiểm tra xem file có quyền ghi không.
-x file: Kiểm tra xem file có quyền thực thi không.
Các toán tử này được kết hợp với toán tử logic bên trong [[ ... ]] để tạo ra các điều kiện phức tạp. Ví dụ: [[ $number -gt 10 && $number -lt 20 ]] để kiểm tra xem một số có nằm trong khoảng (10, 20) hay không.

Ví dụ minh họa kiểm tra điều kiện trong script
Lý thuyết sẽ dễ hiểu hơn rất nhiều khi đi kèm với một ví dụ thực tế. Hãy cùng viết một kịch bản đơn giản yêu cầu người dùng nhập tuổi và kiểm tra xem họ có đủ tuổi để bầu cử hay không (giả sử tuổi yêu cầu là 18) và liệu tuổi đó có hợp lệ không (lớn hơn 0).
#!/bin/bash
# Yêu cầu người dùng nhập tuổi
read -p "Vui lòng nhập tuổi của bạn: " age
# Kiểm tra xem đầu vào có phải là một số nguyên hay không
if ! [[ "$age" =~ ^[0-9]+$ ]]; then
echo "Lỗi: Vui lòng nhập một số hợp lệ."
exit 1
fi
# Kiểm tra điều kiện tuổi bằng toán tử so sánh và logic
if [[ "$age" -ge 18 && "$age" -lt 120 ]]; then
echo "Bạn đã đủ tuổi để tham gia bầu cử."
elif [[ "$age" -gt 0 && "$age" -lt 18 ]]; then
echo "Bạn chưa đủ tuổi để tham gia bầu cử."
else
echo "Tuổi bạn nhập không hợp lệ."
fi
Hãy cùng phân tích chi tiết kịch bản trên:
read -p "..." age: Hiển thị một lời nhắc và đọc dữ liệu người dùng nhập vào biến age.
if ! [[ "$age" =~ ^[0-9]+$ ]]: Đây là bước kiểm tra đầu vào. [[ ... =~ ... ]] là toán tử so khớp biểu thức chính quy. ^[0-9]+$ kiểm tra xem chuỗi có phải chỉ chứa các chữ số hay không. Toán tử ! đảo ngược kết quả, nghĩa là “nếu chuỗi nhập vào không phải là số” thì sẽ thông báo lỗi và thoát kịch bản.
if [[ "$age" -ge 18 && "$age" -lt 120 ]]: Đây là lúc chúng ta kết hợp toán tử. Script kiểm tra hai điều kiện cùng lúc: tuổi phải lớn hơn hoặc bằng 18 (-ge 18) VÀ (&&) tuổi phải nhỏ hơn 120 (giả sử là một giới hạn hợp lý). Cả hai điều kiện phải đúng thì thông báo đầu tiên mới được in ra.
elif [[ "$age" -gt 0 && "$age" -lt 18 ]]: Nếu điều kiện đầu tiên sai, script sẽ kiểm tra tiếp điều kiện này. Nó kiểm tra xem tuổi có lớn hơn 0 VÀ nhỏ hơn 18 không.
else: Nếu tất cả các điều kiện trên đều sai, khối lệnh này sẽ được thực thi, cho biết tuổi không hợp lệ.
Qua ví dụ này, bạn có thể thấy cách các toán tử so sánh (-ge, -lt, -gt) và toán tử logic (&&, !) phối hợp với nhau để tạo ra một luồng chương trình rõ ràng và xử lý nhiều trường hợp khác nhau.

Ví dụ thực hành và cách kết hợp toán tử logic trong shell script
Hiểu rõ khái niệm là bước đầu tiên, nhưng việc áp dụng vào các kịch bản thực tế mới thực sự giúp bạn làm chủ kiến thức. Trong phần này, chúng ta sẽ xây dựng các ví dụ phức tạp hơn, mô phỏng những tác vụ mà bạn có thể gặp hàng ngày khi làm việc với shell script, từ đó thấy rõ sức mạnh của việc kết hợp các toán tử logic.
Kịch bản thực tế sử dụng && và ||
Hãy tưởng tượng bạn cần viết một script Bash sao lưu tự động. Kịch bản này cần kiểm tra xem thư mục nguồn có tồn tại không và thư mục đích có quyền ghi không. Nếu cả hai điều kiện đều thỏa mãn, nó sẽ tiến hành sao lưu. Nếu không, nó sẽ báo lỗi. Đây là một ứng dụng hoàn hảo cho toán tử &&.
#!/bin/bash
SOURCE_DIR="/var/www/html"
BACKUP_DIR="/mnt/backups"
TIMESTAMP=$(date +%Y%m%d_%H%M%S)
BACKUP_FILE="$BACKUP_DIR/backup_$TIMESTAMP.tar.gz"
echo "Bắt đầu quá trình sao lưu..."
# Kiểm tra thư mục nguồn tồn tại VÀ thư mục đích có quyền ghi
if [[ -d "$SOURCE_DIR" && -w "$BACKUP_DIR" ]]; then
echo "Điều kiện hợp lệ. Đang tiến hành nén và sao lưu..."
# Lệnh tar thực hiện sao lưu. Nếu thành công, thông báo hoàn tất.
tar -czf "$BACKUP_FILE" "$SOURCE_DIR" && echo "Sao lưu thành công! File được lưu tại: $BACKUP_FILE"
else
# Nếu một trong hai điều kiện ban đầu sai, kiểm tra lại từng cái để báo lỗi cụ thể
[[ ! -d "$SOURCE_DIR" ]] && echo "Lỗi: Thư mục nguồn $SOURCE_DIR không tồn tại."
[[ ! -w "$BACKUP_DIR" ]] && echo "Lỗi: Không có quyền ghi vào thư mục đích $BACKUP_DIR."
exit 1
fi
echo "Quá trình sao lưu kết thúc."
Trong kịch bản này, [[ -d "$SOURCE_DIR" && -w "$BACKUP_DIR" ]] đảm bảo rằng script chỉ tiếp tục khi cả hai điều kiện tiên quyết đều được đáp ứng. Bên trong khối if, chúng ta lại sử dụng && một lần nữa: tar ... && echo .... Lệnh echo "Sao lưu thành công!" chỉ được thực thi nếu lệnh tar hoàn thành mà không có lỗi. Điều này giúp bạn kiểm soát luồng chương trình một cách chặt chẽ, chỉ thông báo thành công khi công việc thực sự đã hoàn thành.

Kết hợp nhiều toán tử để kiểm soát luồng chương trình
Trong thực tế, các điều kiện thường phức tạp hơn nhiều. Bạn có thể cần kiểm tra nhiều khả năng khác nhau. Đây là lúc việc nhóm các điều kiện bằng dấu ngoặc đơn ( ... ) bên trong [[ ... ]] trở nên vô cùng hữu ích. Nó cho phép bạn xác định rõ thứ tự ưu tiên của các phép toán logic.
Hãy xem xét một ví dụ: một kịch bản cần kiểm tra xem người dùng hiện tại có phải là root hoặc là thành viên của nhóm developers hay không. Đồng thời, kịch bản chỉ nên chạy vào các ngày trong tuần (từ thứ Hai đến thứ Sáu). Đây là một kịch bản có thể dùng để chạy các tác vụ bảo trì định kỳ chỉ dành cho người dùng có quyền và chỉ trong giờ làm việc.
#!/bin/bash
# Lấy ngày hiện tại trong tuần (1=Thứ Hai, ..., 7=Chủ Nhật)
DAY_OF_WEEK=$(date +%u)
# Kiểm tra tư cách thành viên nhóm 'developers'
is_developer() {
groups "$USER" | grep -q '\bdevelopers\b'
}
echo "Kiểm tra quyền và lịch chạy..."
# Điều kiện phức hợp: (là root HOẶC là developer) VÀ (là ngày trong tuần)
if [[ ( "$USER" == "root" || $(is_developer) ) && ( "$DAY_OF_WEEK" -ge 1 && "$DAY_OF_WEEK" -le 5 ) ]]; then
echo "Xác thực thành công. Bắt đầu thực thi tác vụ."
# ... đặt các lệnh của bạn ở đây ...
echo "Tác vụ đã hoàn thành."
else
echo "Lỗi: Không đủ quyền hoặc không phải ngày chạy tác vụ."
if ! [[ "$USER" == "root" || $(is_developer) ]]; then
echo "-> Lý do: Bạn không phải là 'root' hoặc thành viên nhóm 'developers'."
fi
if ! [[ "$DAY_OF_WEEK" -ge 1 && "$DAY_OF_WEEK" -le 5 ]]; then
echo "-> Lý do: Hôm nay không phải ngày trong tuần."
fi
exit 1
fi
Trong ví dụ này, logic cốt lõi nằm ở dòng if. Hãy phân tích nó:
( "$USER" == "root" || $(is_developer) ): Dấu ngoặc đơn nhóm điều kiện OR lại với nhau. Script sẽ kiểm tra xem tên người dùng có phải là “root” không, HOẶC kết quả của hàm is_developer có thành công không. Kết quả của toàn bộ nhóm này sẽ là ĐÚNG nếu một trong hai điều kiện là đúng.
( "$DAY_OF_WEEK" -ge 1 && "$DAY_OF_WEEK" -le 5 ): Nhóm này kiểm tra xem ngày trong tuần có nằm trong khoảng từ 1 đến 5 hay không.
&&: Toán tử AND ở giữa kết nối hai nhóm trên. Điều này có nghĩa là toàn bộ biểu thức if chỉ đúng khi: (kết quả của nhóm kiểm tra người dùng là ĐÚNG) VÀ (kết quả của nhóm kiểm tra ngày là ĐÚNG).
Việc sử dụng nhóm điều kiện giúp logic của bạn trở nên cực kỳ rõ ràng, dễ đọc và dễ bảo trì, tránh được những sai lầm do thứ tự ưu tiên mặc định của toán tử.

Các vấn đề phổ biến và cách xử lý
Khi mới làm quen với toán tử logic trong Bash, việc gặp lỗi là điều khó tránh khỏi. Hiểu được những cạm bẫy phổ biến và cách khắc phục sẽ giúp bạn tiết kiệm rất nhiều thời gian gỡ lỗi và viết mã nguồn sạch hơn, ổn định hơn ngay từ đầu.
Lỗi do không hiểu rõ ưu tiên toán tử
Một trong những lỗi logic khó phát hiện nhất xuất phát từ việc không nắm rõ thứ tự ưu tiên của các toán tử. Trong cấu trúc [[ ... ]], toán tử && (AND) có độ ưu tiên cao hơn || (OR). Điều này có nghĩa là các phép toán AND sẽ được thực hiện trước các phép toán OR, tương tự như phép nhân và phép cộng trong toán học.
Hãy xem xét một điều kiện sau: [[ "$role" == "admin" || "$role" == "editor" && "$is_active" == "true" ]]. Một người mới có thể đọc điều này là “(role là admin hoặc editor) và (trạng thái là active)”. Nhưng thực tế, Bash sẽ hiểu nó là “role là admin HOẶC (role là editor VÀ trạng thái là active)”. Điều này dẫn đến kết quả hoàn toàn khác: một admin dù không active vẫn sẽ thỏa mãn điều kiện.
Cách giải quyết triệt để vấn đề này là luôn sử dụng dấu ngoặc đơn ( ... ) để nhóm các điều kiện một cách tường minh, thể hiện rõ ý định của bạn. Câu lệnh trên nên được viết lại như sau để đảm bảo logic đúng:
[[ ( "$role" == "admin" || "$role" == "editor" ) && "$is_active" == "true" ]]
Bằng cách này, bạn buộc Bash phải đánh giá biểu thức OR trước, sau đó mới lấy kết quả đó để thực hiện phép toán AND. Quy tắc vàng là: khi có sự kết hợp giữa && và || trong cùng một biểu thức, hãy dùng ngoặc để đảm bảo không có sự nhầm lẫn nào.

Kết quả kiểm tra điều kiện không như mong đợi
Đôi khi script của bạn chạy không báo lỗi cú pháp nhưng lại cho ra kết quả sai. Nguyên nhân thường nằm ở những chi tiết nhỏ trong cách bạn viết biểu thức điều kiện. Dưới đây là một số thủ phạm phổ biến và cách phòng tránh.
1. Thiếu dấu ngoặc kép cho biến: Đây là lỗi kinh điển nhất. Nếu một biến chứa khoảng trắng hoặc là một chuỗi rỗng, việc không đặt nó trong dấu ngoặc kép ("$variable") có thể làm hỏng biểu thức test hoặc [ ... ]. Ví dụ, nếu biến filename là “my file.txt”, lệnh [ -f $filename ] sẽ bị Bash diễn giải thành [ -f my file.txt ], gây ra lỗi cú pháp “too many arguments”. Luôn sử dụng [[ -f "$filename" ]] để tránh vấn đề này. Cấu trúc [[ ... ]] xử lý việc này tốt hơn, nhưng việc tập thói quen dùng ngoặc kép là một phương pháp lập trình an toàn.
2. Nhầm lẫn giữa toán tử so sánh chuỗi và số: Dùng = để so sánh số nguyên sẽ không hoạt động như bạn mong đợi. Bash sẽ coi đó là so sánh chuỗi. Ví dụ, [[ "10" > "2" ]] là đúng vì theo thứ tự từ điển, “1” đứng trước “2”. Để so sánh số, bạn phải dùng -gt (lớn hơn): [[ 10 -gt 2 ]].
3. Sai cú pháp khoảng trắng: Bash rất nhạy cảm với khoảng trắng. Viết liền [[$var==1]] sẽ gây lỗi. Cú pháp đúng đòi hỏi phải có khoảng trắng ngăn cách các toán tử và dấu ngoặc: [[ "$var" == 1 ]].
Cách gỡ lỗi (debug):
- Sử dụng
set -x: Đặt lệnh set -x ở đầu script của bạn. Khi chạy, Bash sẽ in ra từng lệnh mà nó thực thi sau khi đã thay thế các biến. Điều này giúp bạn thấy chính xác biểu thức điều kiện của mình trông như thế nào trước khi được đánh giá. Dùng set +x để tắt chế độ này.
- In giá trị biến: Ngay trước câu lệnh
if, hãy dùng echo để in ra giá trị của tất cả các biến liên quan. Ví dụ: echo "DEBUG: role=$role, is_active=$is_active". Điều này giúp xác nhận rằng các biến đang chứa giá trị mà bạn mong đợi.
- Kiểm tra từng phần: Nếu bạn có một điều kiện phức tạp, hãy tách nó ra thành các câu lệnh
if đơn giản hơn để xác định chính xác phần nào đang gây ra lỗi logic.
Lời khuyên và mẹo nâng cao khi sử dụng toán tử logic trong Bash
Sau khi đã nắm vững những kiến thức cơ bản và cách xử lý các lỗi thường gặp, đây là lúc để nâng tầm kỹ năng viết script của bạn. Những mẹo và lời khuyên dưới đây sẽ giúp bạn viết mã nguồn không chỉ hoạt động đúng mà còn rõ ràng, dễ bảo trì và hiệu quả hơn.
- Sử dụng ngoặc đơn và ngoặc nhọn hợp lý: Như đã nhấn mạnh, luôn dùng ngoặc đơn
( ... ) bên trong [[ ... ]] để nhóm các điều kiện khi kết hợp && và ||. Điều này loại bỏ mọi sự mơ hồ về thứ tự ưu tiên và làm cho ý định của bạn trở nên rõ ràng đối với bất kỳ ai đọc mã nguồn sau này.
- Tránh lạm dụng sự ngắn gọn: Toán tử
&& và || cho phép viết các chuỗi lệnh trên một dòng rất ngắn gọn. Tuy nhiên, một chuỗi lệnh như command1 && command2 || command3 && command4 có thể trở nên rất khó đọc và khó gỡ lỗi. Trong những trường hợp phức tạp, việc sử dụng cấu trúc if/elif/else đầy đủ sẽ giúp mã nguồn của bạn dễ hiểu hơn rất nhiều. Hãy ưu tiên sự rõ ràng hơn là sự ngắn gọn một cách cực đoan.
- Thường xuyên kiểm tra và test điều kiện: Đừng cho rằng script của bạn sẽ luôn chạy trong một môi trường lý tưởng. Hãy viết các bài kiểm tra (test case) cho script của bạn. Thử nghiệm với các giá trị đầu vào khác nhau: chuỗi rỗng, chuỗi có khoảng trắng, số âm, file không tồn tại, file không có quyền đọc… Việc phát hiện lỗi sớm trong quá trình phát triển sẽ dễ dàng hơn nhiều so với khi script đã được triển khai.
- Sử dụng biến trung gian để tăng tính rõ ràng: Đối với các điều kiện logic rất phức tạp, việc gán kết quả của các phần kiểm tra vào các biến trung gian có tên gợi nhớ có thể cải thiện đáng kể khả năng đọc hiểu.
Ví dụ, thay vì viết:
if [[ ( "$USER" == "root" || $(groups | grep -q '\badmin\b') ) && ( $(date +%u) -le 5 && $(date +%H) -ge 9 && $(date +%H) -lt 17 ) ]]; then ... fi
Bạn có thể viết lại như sau:
is_privileged_user=false
if [[ "$USER" == "root" || $(groups | grep -q '\badmin\b') ]]; then
is_privileged_user=true
fi
is_working_hours=false
day=$(date +%u)
hour=$(date +%H)
if [[ "$day" -le 5 && "$hour" -ge 9 && "$hour" -lt 17 ]]; then
is_working_hours=true
fi
if [[ "$is_privileged_user" == "true" && "$is_working_hours" == "true" ]]; then
echo "Điều kiện hợp lệ. Bắt đầu công việc."
fi
Mặc dù dài hơn, phiên bản thứ hai rõ ràng hơn rất nhiều. Bạn có thể dễ dàng hiểu được mục đích của từng khối logic và gỡ lỗi từng phần một cách độc lập.
- Ưu tiên
[[ ... ]] hơn [ ... ]: Hãy tập thói quen sử dụng dấu ngoặc kép [[ ... ]]. Nó là một phiên bản cải tiến của [ ... ], cung cấp nhiều tính năng hơn (như so khớp biểu thức chính quy với =~), an toàn hơn (không bị ảnh hưởng bởi word splitting và filename expansion) và có cú pháp xử lý các toán tử logic &&, || tự nhiên hơn.

Kết luận
Qua bài viết này, chúng ta đã cùng nhau khám phá một trong những khía cạnh nền tảng nhưng vô cùng quan trọng của Bash shell scripting: toán tử logic. Từ việc hiểu rõ cách hoạt động của && (AND), || (OR), và ! (NOT) dựa trên trạng thái kết thúc của lệnh, cho đến việc kết hợp chúng với các toán tử so sánh để xây dựng những điều kiện phức tạp, bạn đã có trong tay bộ công cụ cần thiết để điều khiển luồng chương trình một cách thông minh và linh hoạt. Việc nắm vững những khái niệm này chính là chìa khóa để chuyển từ việc viết những script tuần tự đơn giản sang việc xây dựng các công cụ tự động hóa mạnh mẽ, có khả năng ra quyết định và xử lý lỗi một cách chuyên nghiệp.
Hãy nhớ rằng, học đi đôi với hành. Lý thuyết sẽ chỉ thực sự trở thành kỹ năng khi bạn áp dụng nó vào thực tế. Đừng ngần ngại mở trình soạn thảo và bắt đầu viết những kịch bản của riêng mình. Hãy thử thách bản thân bằng cách tự động hóa một công việc lặp đi lặp lại hàng ngày, sử dụng các toán tử logic để xử lý các trường hợp khác nhau có thể xảy ra. Càng thực hành nhiều, bạn sẽ càng trở nên thành thạo và tự tin hơn. Chúc bạn thành công trên hành trình chinh phục Bash scripting và tự động hóa hệ thống!