Sự khác biệt giữa dấu ngoặc kép và dấu ngoặc đơn trong bash là gì?


426

Tôi chỉ tự hỏi chính xác sự khác biệt giữa

[[ $STRING != foo ]]

[ $STRING != foo ]

là, ngoài cái sau đó là tuân thủ posix, được tìm thấy trong sh và cái trước là một phần mở rộng được tìm thấy trong bash.


1
Trong trường hợp bạn cũng đang tự hỏi về việc không sử dụng dấu ngoặc nào, ví dụ: trong ngữ cảnh của một iftuyên bố, hãy xem mywiki.wooledge.org/BashPit thác # if_.5Bgrep_foo_myfile.5D
Kev

cũng, Từ tài liệu của Ubuntu: wiki.ubuntu.com/...
radistao

Câu trả lời:


309

Có một số khác biệt. Theo tôi, một vài trong số quan trọng nhất là:

  1. [là một nội dung trong Bash và nhiều vỏ hiện đại khác. Nội dung [tương tự testvới yêu cầu bổ sung của việc đóng ]. Các nội trang [testbắt chước chức năng /bin/[/bin/testcùng với các hạn chế của chúng để các tập lệnh sẽ tương thích ngược. Các tệp thực thi ban đầu vẫn tồn tại chủ yếu để tuân thủ POSIX và tương thích ngược. Chạy lệnh type [trong Bash chỉ ra rằng [được hiểu là một dựng sẵn theo mặc định. (Lưu ý: which [chỉ tìm các tệp thực thi trên PATH và tương đương với type -p [)
  2. [[không tương thích, nó sẽ không nhất thiết phải hoạt động với bất kỳ /bin/shđiểm nào. Vì vậy, [[tùy chọn Bash / Zsh / Ksh hiện đại hơn.
  3. Do [[được tích hợp vào trình bao và không có các yêu cầu cũ, bạn không cần phải lo lắng về việc phân tách từ dựa trên biến IFS để làm rối tung các biến đánh giá thành một chuỗi có khoảng trắng. Do đó, bạn không thực sự cần phải đặt biến trong dấu ngoặc kép.

Đối với hầu hết các phần, phần còn lại chỉ là một số cú pháp đẹp hơn. Để thấy nhiều sự khác biệt hơn, tôi khuyên bạn nên liên kết này với câu trả lời Câu hỏi thường gặp: Sự khác biệt giữa kiểm tra, [và [[? . Trong thực tế, nếu bạn nghiêm túc về bash scripting, tôi khuyên bạn nên đọc toàn bộ wiki , bao gồm cả FAQ, Cạm bẫy và Hướng dẫn. Phần kiểm tra từ phần hướng dẫn cũng giải thích những khác biệt này và tại sao (các) tác giả nghĩ [[là lựa chọn tốt hơn nếu bạn không cần phải lo lắng về việc có thể mang theo được. Những lý do chính là:

  1. Bạn không phải lo lắng về việc trích dẫn phía bên trái của bài kiểm tra để nó thực sự được đọc dưới dạng một biến.
  2. Bạn không phải thoát ít hơn và lớn hơn < >với dấu gạch chéo ngược để chúng không được đánh giá là chuyển hướng đầu vào, điều này thực sự có thể làm rối tung một số nội dung bằng cách ghi đè tệp. Điều này một lần nữa trở lại [[là một nội dung. Nếu [(kiểm tra) là một chương trình bên ngoài, trình bao sẽ phải tạo một ngoại lệ theo cách nó đánh giá <>chỉ khi /bin/testđược gọi, điều đó sẽ không thực sự có ý nghĩa.

5
Cảm ơn, liên kết đến bash FAQ là những gì tôi đang tìm kiếm (không biết về trang đó, cảm ơn).
0x89

2
Tôi đã chỉnh sửa bài đăng của bạn với thông tin này, nhưng [và kiểm tra được thực thi dưới dạng nội trang. Các nội trang được thiết kế để thay thế / bin / [và / bin / test nhưng cũng cần tái tạo các giới hạn của các nhị phân. Lệnh 'type [' xác minh rằng nội dung được sử dụng. 'Mà [' chỉ tìm kiếm các tệp thực thi trên PATH và tương đương với 'loại -P ['
klynch

133

Nói ngắn gọn:

[là một bash Buildin

[[]] là từ khóa bash

Từ khóa: Từ khóa khá giống các nội trang, nhưng sự khác biệt chính là các quy tắc phân tích cú pháp đặc biệt áp dụng cho chúng. Ví dụ: [là một bash dựng sẵn, trong khi [[là một từ khóa bash. Cả hai đều được sử dụng để thử nghiệm công cụ, nhưng vì [[là một từ khóa chứ không phải là nội dung, nên nó được hưởng lợi từ một vài quy tắc phân tích cú pháp đặc biệt giúp việc này dễ dàng hơn nhiều:

  $ [ a < b ]
 -bash: b: No such file or directory
  $ [[ a < b ]]

Ví dụ đầu tiên trả về lỗi vì bash cố gắng chuyển hướng tệp b sang lệnh [a]. Ví dụ thứ hai thực sự làm những gì bạn mong đợi nó. Ký tự <không còn có ý nghĩa đặc biệt của toán tử Chuyển hướng tệp.

Nguồn: http://mywiki.wooledge.org/BashGuide/CommandsAndArgument


3
[là một lệnh shell POSIX; nó không cần phải được xây dựng. ]chỉ là một đối số mà lệnh đó tìm kiếm, để cú pháp được cân bằng. Lệnh này là một từ đồng nghĩa với testngoại trừ testkhông tìm cách đóng ].
Kaz


81

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

Một số khác biệt về 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 &&||nối 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 trái
    • [ ( 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, bởi 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.


Làm thế nào để sử dụng printf 'ab' | grep -Eq 'ab?'trong if [ … ]?
meeDamian

1
@meeDamian if ( printf 'ab' | grep -Eq 'a' ); then echo 'a'; fi. []là một lệnh giống như grep. Điều ()này có thể không cần thiết cho lệnh đó Tôi không chắc chắn: Tôi đã thêm nó vì |, phụ thuộc vào cách Bash phân tích mọi thứ. Nếu không có |tôi chắc chắn bạn có thể viết if cmd arg arg; then.
Ciro Santilli 心 心 事件

1
@meeDamian yeah, không có nhu cầu ()có vẻ như: stackoverflow.com/questions/8965509/...
Ciro Santilli新疆改造中心法轮功六四事件

1
Danh sách tốt đẹp! Xem thêm: wiki.ubuntu.com/...
radistao

5

Giá đỡ đơn tức []là vỏ POSIX tuân thủ để kèm theo biểu thức điều kiện.

Double Brackets tức [[]]là phiên bản nâng cao (hoặc mở rộng) của phiên bản POSIX tiêu chuẩn, phiên bản này được hỗ trợ bởi bash và các shell khác (zsh, ksh).

Trong bash, để so sánh số lượng chúng tôi sử dụng eq, ne, ltgt, với dấu ngoặc kép để so sánh chúng ta có thể sử dụng ==, !=, <,>theo nghĩa đen.

  • [là một từ đồng nghĩa với lệnh kiểm tra. Ngay cả khi nó được tích hợp vào vỏ, nó sẽ tạo ra một quy trình mới.
  • [[ là một phiên bản cải tiến mới của nó, là một từ khóa, không phải là một chương trình.

ví dụ:

[ var1 lt var2] #works
[ var1 < var2] #error: var2 No such file or directory 
[ var1 \< var2] #works with escape
[[ var1 < var2]] #works

4

Dựa trên việc đọc nhanh các phần có liên quan của trang, sự khác biệt chính có vẻ là các toán tử ==!=toán tử khớp với một mẫu, chứ không phải là một chuỗi ký tự, và cũng có =~toán tử so sánh regex.

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.