Sự khác biệt giữa dấu ngoặc vuông đơn và đôi trong Bash


161

Tôi đang đọc các ví dụ bash về ifnhưng một số ví dụ được viết với dấu ngoặc đơn:

if [ -f $param ]
then
  #...
fi

những người khác với dấu ngoặc vuông:

if [[ $? -ne 0 ]]
then
    start looking for errors in yourlog
fi

Sự khác biệt là gì?


3
Bạn có thể nhận được câu trả lời của mình bằng cách xem câu trả lời của câu hỏi này: unix.stackexchange.com/questions/3831/iêu
Amir Naghizadeh

Câu trả lời:


188

Đơn []là các thử nghiệm điều kiện tuân thủ vỏ posix.

Double [[]]là một phần mở rộng cho tiêu chuẩn []và được hỗ trợ bởi bash và các shell khác (ví dụ zsh, ksh). Họ hỗ trợ các hoạt động bổ sung (cũng như các hoạt động posix tiêu chuẩn). Ví dụ: ||thay vì -ovà regex khớp với =~. Một danh sách đầy đủ hơn về sự khác biệt có thể được tìm thấy trong phần hướng dẫn sử dụng bash trên các cấu trúc có điều kiện .

Sử dụng []bất cứ khi nào bạn muốn tập lệnh của bạn có thể di động trên các shell. Sử dụng [[]]nếu bạn muốn các biểu thức điều kiện không được hỗ trợ bởi []và không cần phải di động.


6
Tôi muốn thêm rằng nếu tập lệnh của bạn không bắt đầu với một shebang yêu cầu rõ ràng một trình bao hỗ trợ [[ ]](ví dụ: bash với #!/bin/bashhoặc #!/usr/bin/env bash), bạn nên sử dụng tùy chọn di động. Các tập lệnh giả định / bin / sh hỗ trợ các tiện ích mở rộng như thế này sẽ phá vỡ các hệ điều hành như các bản phát hành Debian và Ubuntu gần đây trong trường hợp không phải vậy.
Gordon Davisson

87

Khác biệt về hành vi

Đã thử nghiệm trong Bash 4.3.11:

  • Gia hạn POSIX vs Bash:

  • lệnh thường xuyên vs ma thuật

    • [ chỉ là một lệnh thông thường với một cái tên kỳ lạ.

      ]chỉ là một đối số [ngăn chặn các đối số tiếp theo được sử dụng.

      Ubuntu 16.04 thực sự có một tệp thực thi cho nó /usr/bin/[được cung cấp bởi coreutils, nhưng phiên bản tích hợp bash được ưu tiên.

      Không có gì được thay đổi theo cách Bash phân tích lệnh.

      Cụ thể, <là chuyển hướng &&||ghép nhiều lệnh, ( )tạo các lớp con trừ khi thoát ra \và mở rộng từ diễn ra như bình thường.

    • [[ X ]]là một cấu trúc duy nhất Xđược phân tích cú pháp một cách kỳ diệu. <, &&, ||()được đối xử đặc biệt, và các quy tắc từ tách khác nhau.

      Ngoài ra còn có sự khác biệt như ==~.

      Trong Bashese: [là một lệnh tích hợp và [[là một từ khóa: https://askubfox.com/questions/445749/whats-the-difference-b between-shell-buildin-and-shell - keyword

  • <

  • &&||

    • [[ a = a && b = b ]]: đúng, hợp lý
    • [ a = a && b = b ]: lỗi cú pháp, được &&phân tích cú pháp như một dấu tách lệnh ANDcmd1 && cmd2
    • [ a = a -a b = b ]: tương đương, nhưng không được chấp nhận bởi POSIX³
    • [ a = a ] && [ b = b ]: POSIX và tương đương đáng tin cậy
  • (

    • [[ (a = a || a = b) && a = b ]]: sai
    • [ ( a = a ) ]: lỗi cú pháp, ()được hiểu là một nhánh con
    • [ \( a = a -o a = b \) -a a = b ]: tương đương, nhưng ()không được chấp nhận bởi POSIX
    • { [ a = a ] || [ a = b ]; } && [ a = b ]POSIX tương đương 5
  • tách từ và tạo tên tệp khi mở rộng (split + global)

    • x='a b'; [[ $x = 'a b' ]]: đúng, trích dẫn không cần thiết
    • x='a b'; [ $x = 'a b' ]: lỗi cú pháp, mở rộng thành [ a b = 'a b' ]
    • x='*'; [ $x = 'a b' ]: lỗi cú pháp nếu có nhiều hơn một tệp trong thư mục hiện tại.
    • x='a b'; [ "$x" = 'a b' ]: Tương đương POSIX
  • =

    • [[ ab = a? ]]: đúng, vì nó không khớp mẫu ( * ? [là ma thuật). Không toàn cầu mở rộng để tập tin trong thư mục hiện tại.
    • [ ab = a? ]: toàn a?cầu mở rộng. Vì vậy, có thể đúng hoặc sai tùy thuộc vào các tệp trong thư mục hiện tại.
    • [ ab = a\? ]: sai, không mở rộng toàn cầu
    • ===giống nhau ở cả hai [[[, nhưng ==là một phần mở rộng Bash.
    • case ab in (a?) echo match; esac: Tương đương POSIX
    • [[ ab =~ 'ab?' ]]: sai 4 , mất phép thuật với''
    • [[ ab? =~ 'ab?' ]]: thật
  • =~

    • [[ ab =~ ab? ]]: đúng, POSIX kết hợp biểu thức chính quy mở rộng , ?không mở rộng toàn cầu
    • [ a =~ a ]: lỗi cú pháp. Không có bash tương đương.
    • printf 'ab\n' | grep -Eq 'ab?': Tương đương POSIX (chỉ dữ liệu một dòng)
    • awk 'BEGIN{exit !(ARGV[1] ~ ARGV[2])}' ab 'ab?': Tương đương POSIX.

Khuyến cáo : luôn luôn sử dụng [].

Có tương đương POSIX cho mọi [[ ]]cấu trúc tôi đã thấy.

Nếu bạn sử dụng [[ ]]bạn:

  • mất tính di động
  • buộc người đọc phải tìm hiểu sự phức tạp của một phần mở rộng bash khác. [chỉ là một lệnh thông thường với một cái tên kỳ lạ, không có ngữ nghĩa đặc biệt nào được tham gia.

Lấy cảm hứng từ [[...]]cấu trúc tương đương trong vỏ Korn

² nhưng không thành công đối với một số giá trị ahoặc b(như +hoặc index) và không so sánh số nếu abtrông giống như số nguyên thập phân. expr "x$a" '<' "x$b"làm việc xung quanh cả hai.

Và cũng thất bại đối với một số giá trị ahoặc bthích !hoặc (.

4 trong bash 3.2 trở lên và cung cấp khả năng tương thích với bash 3.1 không được bật (như với BASH_COMPAT=3.1)

5 mặc dù việc nhóm (ở đây với {...;}nhóm lệnh thay vì (...)chạy một lớp con không cần thiết) là không cần thiết vì các toán tử shell ||&&(trái ngược với các toán tử ||&& [[...]]toán tử -o/ -a [toán tử) có quyền ưu tiên như nhau. Như vậy [ a = a ] || [ a = b ] && [ a = b ]sẽ tương đương.


3
" Có tương đương POSIX cho mọi [[]] cấu trúc mà tôi đã thấy. " Điều tương tự có thể được nói về bất kỳ ngôn ngữ Turing Complete nào trên bề mặt hành tinh.
A. Rick

3
@ A.Nhấp đó sẽ là một câu trả lời hợp lệ cho tất cả các câu hỏi "Làm thế nào để thực hiện X bằng ngôn ngữ Y" :-) Tất nhiên, có một câu "tiện lợi" ẩn trong câu nói đó.
Ciro Santilli 郝海东 冠状 病 事件 法轮功

6
Tóm tắt tuyệt vời. Cảm ơn cho những nỗ lực. Tuy nhiên tôi không đồng ý với khuyến nghị. Đó là tính di động so với cú pháp phù hợp và mạnh mẽ hơn. Nếu bạn có thể yêu cầu bash> 4 trong môi trường của mình thì cú pháp [[]] được khuyến nghị.
Bernard

@Downvoters vui lòng giải thích để tôi có thể tìm hiểu và cải thiện thông tin :-)
Ciro Santilli 冠状 病 六四 事件

8
Câu trả lời tuyệt vời nhưng tôi nghĩ Khuyến nghị : luôn luôn[] nên đọc là Sở thích của tôi : sử dụng []nếu bạn không muốn mất tính di động . Như đã nêu ở đây : Nếu tính di động / tuân thủ POSIX hoặc BourneShell là một mối quan tâm, nên sử dụng cú pháp cũ. Mặt khác, tập lệnh yêu cầu BASH, Zsh hoặc KornShell, cú pháp mới thường linh hoạt hơn, nhưng không nhất thiết phải tương thích ngược. Tôi thà đi cùng [[ ab =~ ab? ]]nếu tôi có thể và không phải lo lắng về khả năng tương thích ngược hơnprintf 'ab' | grep -Eq 'ab?'
MauricioRobayo

14

Bên trong dấu ngoặc đơn để kiểm tra điều kiện (ví dụ [...]), một số toán tử như đơn =được hỗ trợ bởi tất cả các shell, trong khi việc sử dụng toán tử ==không được hỗ trợ bởi một số shell cũ.

Bên trong dấu ngoặc kép để kiểm tra điều kiện (tức là [[...]]), không có sự khác biệt giữa việc sử dụng =hoặc ==trong vỏ cũ hoặc mới.

Chỉnh sửa: Tôi cũng cần lưu ý rằng: Trong bash, luôn luôn sử dụng dấu ngoặc kép [[...]] nếu có thể, vì nó an toàn hơn dấu ngoặc đơn. Tôi sẽ minh họa tại sao với ví dụ sau:

if [ $var == "hello" ]; then

nếu $ var xảy ra là null / trống, thì đây là những gì tập lệnh nhìn thấy:

if [ == "hello" ]; then

Điều này sẽ phá vỡ kịch bản của bạn. Giải pháp là sử dụng dấu ngoặc kép hoặc luôn nhớ đặt dấu ngoặc kép quanh các biến của bạn ( "$var"). Dấu ngoặc kép là thực hành mã hóa phòng thủ tốt hơn.


1
Đặt dấu ngoặc kép xung quanh tất cả các lần đọc biến trừ khi bạn có lý do rất chính đáng không phải là cách thực hành mã hóa phòng thủ tốt hơn nhiều , vì nó áp dụng cho tất cả các lần đọc biến, không chỉ những điều kiện trong điều kiện. Một lỗi trình cài đặt iTunes đã xóa các tệp của mọi người nếu tên ổ cứng chứa khoảng trắng (hoặc đại loại như thế). Nó cũng giải quyết vấn đề bạn đề cập.
Chai T. Rex


1

bạn có thể sử dụng dấu ngoặc vuông đôi để khớp regex ánh sáng, ví dụ:

if [[ $1 =~ "foo.*bar" ]] ; then

(miễn là phiên bản bash bạn đang sử dụng hỗ trợ cú pháp này)


6
Ngoại trừ bạn đã trích dẫn mẫu, vì vậy bây giờ nó được coi là một chuỗi chữ.
ormaaj

rất đúng. đôi khi điều này làm tôi khó chịu :)
asf107

1

Hướng dẫn Bash nói:

Khi được sử dụng với [[, các toán tử '<' và '>' sắp xếp từ vựng theo ngôn ngữ hiện tại. Lệnh kiểm tra sử dụng thứ tự ASCII.

(Lệnh kiểm tra giống hệt với [])

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.