Sự khác biệt của các toán tử [[ vs [ vs ( vs (( trong shell script

Sự khác biệt của các toán tử [[ vs [ vs ( vs (( trong shell script
Photo by Susan Wilkinson from Unsplash

Để tự động hóa các thao tác trong công việc, shell script chính là một phương pháp rất hiệu quả. Shell script có thể chạy được ở mọi nơi mà không cần cài đặt nhiều (Windows giờ cũng có WSL để chạy Linux shell rồi).

Tuy nhiên, trong bài viết này, tôi không muốn đi sâu vào shell script. Tôi chỉ muốn trình bày những gì mình hiểu về những toán tử thoạt nhìn trông rất giống nhau.

Cú pháp rẽ nhánh của shell script

Khi mới làm quen với shell script, tôi đã gặp khá nhiều khó khăn khi gặp những toán tử này: [[ , [ , ( , ((. Tôi đã từng nhìn thấy những script viết câu điều kiện kiểu như thế này:

if [[ condition ]]

if [ condition ]

if ((condition))

if (condition)

Đây là cú pháp rẽ nhánh của các shell dựa trên sh nguyên thủy. Cú pháp chuẩn của câu lệnh này sẽ kiểu như thế này:

if command1
then
   command2
else
   command3
fi

Shell hoạt động dựa trên exit code (exit code = 0 nếu chương trình kết thúc bình thường, ≠ 0 nếu gặp lỗi). Câu lệnh điều kiện này cũng vậy. Nếu exit code của command1 là 0 thì câu lệnh sau then (command2) sẽ được thực thi. Nếu exit code khác 0 thì câu lệnh sau else (command3) sẽ được thực thi.

Các câu lệnh có thể là một câu lệnh đơn, hoặc kết hợp nhiều lệnh đơn thành một lệnh phức tạp hơn với các toán tử ;, &, &&, || hoặc đơn giản chỉ là xuống dòng.

Việc sử dụng các dấu ngoặc là trường hợp đặc biệt.

Dấu ngoặc vuông [

if [ condition ]

Trong shell script, [ là một lệnh, một lệnh bình thường như bao lệnh khác. Đây có thể coi là một tên khác cho lệnh test nguyên thủy (thêm tham số ] bắt buộc cho đẹp). Cả [test đều được mô tả trong tiêu chuẩn POSIX. Tất cả các shell tương thích với chuẩn POSIX đều có lệnh [ và nó là built-in command (mặc dù điều này không chuẩn theo POSIX).

Chuẩn POSIX yêu cầu lệnh test[ phải là các lệnh độc lập, có file thực thi riêng trong hệ thống. Trong hầu hết các hệ điều hành UNIX và Unix-like, [ tồn tại độc lập (có thể gọi bằng /bin/\[), nhưng khi gọi [ thì lệnh built-in của shell sẽ được thực thi.

Vì là các lệnh của shell nên giữa bắt buộc phải có dấu cách ở sau [ và trước ]. Câu lệnh này cũng sẽ được thực thi và trả về exit code như nhiều lệnh khác. Tất cả các phép so sánh đều là tham số của lệnh này. Câu lệnh này thường được dùng để kiểm tra các điều kiện như so sánh biến hay kiểm tra file có tồn tại hay không.

Hai dấu ngoặc vuông [[

if [[ condition ]]

Cú pháp này ban đầu xuất hiện trên ksh và sau này được một số shell khác như bash, zsh thêm vào. Đây là một phiên bản nâng cấp hơn của [ tiêu chuẩn. Vì không được tiêu chuẩn hóa, nên hoạt động của [[ này không thống nhất giữa các shell.

Thậm chí với bash hay zsh, đây không phải là lệnh mà là keyword của shell. Và nó được thực thi giống như những keyword khác như if, then, fi, v.v…

Một vài nâng cấp có thể kể đến của [[ so với [:

  • Không cần dấu nháy với biến ngay cả khi giá trị có dấu cách
  • Tự động nhận biết tên biến, không cần dấu $
  • Có thể sử dụng dấu == để so sánh trông tự nhiên hơn
  • Có nhiều phép so sánh chuỗi phức tạp như sử dụng ký tự đại diện
  • Có thể sử dụng các phép logic như ||, &&, v.v…

Hai dấu ngoặc tròn ((

if ((condition))

Đây cũng là một cú pháp bắt nguồn từ ksh và sau đó được bash và zsh bổ sung. Đây không phải lệnh, cũng không phải keyword. Tôi cũng không biết gọi nó là gì, nên cứ tạm gọi nó là cú pháp của shell. Việc sử dụng dấu cách sau (( và trước )) không có ảnh hưởng gì.

Đây là cú pháp để thực hiện các phép tính (tính toán số học, logic, phép toán với bit, v.v…). Cũng vì là các phép tính toán, nên exit code của nó hơi đặc biệt. Exit code sẽ bằng 0 nếu phép tính có kết quả khác 0 và ngược lại.

Dấu ngoặc tròn (

if (command)

Đây là cú pháp để chạy lệnh trong subshell (shell con). Khi lệnh thực hiện xong, exit code vẫn được trả về cho câu điều kiện và nó sẽ được thực hiện tiếp.

Cú pháp này không thường xuyên được sử dụng, nhất là với những câu lệnh điều kiện. Chỉ một số ít trường hợp người sử dụng muốn tránh ảnh hưởng từ lệnh tới shell làm việc hiện tại (ví dụ lệnh sẽ gán hoặc thay đổi biến môi trường) sẽ sử dụng cú pháp này. Sau khi thực hiện xong lệnh ở subshell, những thay đổi đó sẽ biến mất và không tác động gì tới shell hiện tại.

Tôi xin lỗi nếu bài viết có bất kỳ typo nào. Nếu bạn nhận thấy điều gì bất thường, xin hãy cho tôi biết.

Nếu có bất điều gì muốn nói, bạn có thể liên hệ với tôi qua các mạng xã hội, tạo discussion hoặc report issue trên Github.

Welcome

manhhomienbienthuy

Đây là thế giới của manhhomienbienthuy (naa). Chào mừng đến với thế giới của tôi!

Bài viết liên quan

Bài viết mới

Chuyên mục

Lưu trữ theo năm

Thông tin liên hệ

Cảm ơn bạn đã quan tâm blog của tôi. Nếu có bất điều gì muốn nói, bạn có thể liên hệ với tôi qua các mạng xã hội, tạo discussion hoặc report issue trên Github.