Sự khác biệt giữa [[$ a == z *]] và [$ a == z *] là gì?


35

Có sự khác biệt nào giữa hai điều này.

[[ $a == z* ]]

[ $a == z* ] 

Tôi có thể có một ví dụ mà họ sẽ có đầu ra khác nhau không?

Hơn nữa, làm việc của [[ ]]khác nhau [ ]như thế nào?

Câu trả lời:


41

Sự khác biệt giữa [[ … ]][ … ]chủ yếu được đề cập trong việc sử dụng dấu ngoặc đơn hoặc dấu ngoặc kép - bash . Điều quan trọng, [[ … ]]là cú pháp đặc biệt, trong khi đó [là một tên trông buồn cười cho một lệnh. [[ … ]]có quy tắc cú pháp đặc biệt cho những gì bên trong, [ … ]không.

Với nếp nhăn được thêm vào của ký tự đại diện, đây là cách [[ $a == z* ]]đánh giá:

  1. Phân tích lệnh: đây là [[ … ]]cấu trúc có điều kiện xung quanh biểu thức điều kiện $a == z*.
  2. Phân tích biểu thức điều kiện: đây là ==toán tử nhị phân, với toán hạng $az*.
  3. Mở rộng toán hạng đầu tiên thành giá trị của biến a.
  4. Đánh giá ==toán tử: kiểm tra xem giá trị của biến có akhớp với mẫu không z*.
  5. Đánh giá biểu thức điều kiện: kết quả của nó là kết quả của toán tử điều kiện.
  6. Lệnh hiện được ước tính, trạng thái của nó là 0 nếu biểu thức điều kiện là true và 1 nếu nó sai.

Đây là cách [ $a == z* ]đánh giá:

  1. Phân tích cú pháp lệnh: đây là [lệnh với các đối số được hình thành bằng cách đánh giá các từ $a, ==, z*, ].
  2. Mở rộng $athành giá trị của biến a.
  3. Thực hiện tách từ và tạo tên tệp trên các tham số của lệnh.
    • Ví dụ, nếu giá trị của alà chuỗi 6 ký tự foo b*(thu được bằng cách ví dụ a='foo b*') và danh sách các tập tin trong thư mục hiện hành được ( bar, baz, qux, zim, zum), sau đó là kết quả của việc mở rộng là danh sách sau đây của chữ: [, foo, bar, baz, ==, zim, zum, ].
  4. Chạy lệnh [với các tham số thu được trong bước trước.
    • Với các giá trị mẫu ở trên, [lệnh phàn nàn về lỗi cú pháp và trả về trạng thái 2.

Lưu ý: Trong [[ $a == z* ]]bước 3, giá trị của aviệc không trải qua quá trình phân tách từ và tạo tên tệp, bởi vì đó là trong bối cảnh mà một từ duy nhất được mong đợi (đối số bên trái của toán tử điều kiện ==). Trong hầu hết các trường hợp, nếu một từ duy nhất có ý nghĩa ở vị trí đó thì việc mở rộng biến sẽ hoạt động giống như trong dấu ngoặc kép. Tuy nhiên, có một ngoại lệ cho quy tắc đó: trong [[ abc == $a ]], nếu giá trị của acác ký tự đại diện, thì ađược khớp với mẫu ký tự đại diện. Ví dụ, nếu giá trị của aa*sau đó [[ abc == $a ]]là đúng (vì ký tự đại diện *đến từ việc mở rộng không thể viện chứng của $atrận đấu *) trong khi [[ abc == "$a" ]]là sai (vì nhân vật bình thường*đến từ việc mở rộng trích dẫn $akhông phù hợp bc). Bên trong [[ … ]], dấu ngoặc kép không tạo sự khác biệt, ngoại trừ ở phía bên tay phải của các nhà khai thác chuỗi khớp ( =, ==, !==~).


39

[là một bí danh cho testlệnh. Phiên bản Unix 6 có iflệnh, nhưng Phiên bản 7 (1979) đi kèm với trình bao Bourne mới có một vài cấu trúc lập trình bao gồm cấu trúc if-then-other-elif-fi và phiên bản Unix 7 đã thêm một testlệnh thực hiện hầu hết các lệnh "Các thử nghiệm" được thực hiện bởi iflệnh trong các phiên bản cũ hơn.

[đã được tạo thành bí danh testvà cả hai đều được tạo sẵn trong hệ vỏ trong Unix System III (1981) . Mặc dù cần lưu ý rằng một số biến thể Unix không có [lệnh cho đến sau đó ( cho đến đầu những năm 2000 trên một số BSD shdựa trên vỏ Almquist (một phần mềm testdựng sẵn luôn được đưa vào ashnguồn, nhưng trên đó BSD ban đầu đã bị vô hiệu hóa)).

Lưu ý rằng testaka [là một lệnh để thực hiện "kiểm tra", không có nhiệm vụ nào mà lệnh đó thực hiện, vì vậy không có lý do gì để phân biệt giữa toán tử gán và toán tử đẳng thức, vì vậy toán tử đẳng thức là =. ==chỉ được hỗ trợ bởi một vài triển khai gần đây của [(và chỉ là bí danh cho =).

Bởi vì [không có gì nhiều hơn một lệnh, nó được phân tích cú pháp giống như bất kỳ lệnh nào khác bằng shell.

Cụ thể, trong ví dụ của bạn $a, vì nó không được trích dẫn, sẽ được chia thành nhiều từ theo quy tắc chia tách từ thông thường và mỗi từ sẽ trải qua quá trình tạo tên tệp hay còn gọi là tạo ra các từ có thể tạo ra nhiều từ hơn, mỗi từ đó dẫn đến một đối số riêng cho [lệnh.

Tương tự, z*sẽ được mở rộng vào danh sách tên tệp trong thư mục hiện tại bắt đầu bằng z.

Vì vậy, ví dụ, nếu $ab* = x, và có z1, z2, b1b2tập tin trong thư mục hiện hành, Các [lệnh sẽ nhận được 9 đối số: [, b1, b2, =, x, ==, z1, z2].

[phân tích các đối số của nó như là một biểu thức điều kiện. 9 đối số đó không thêm vào một biểu thức điều kiện hợp lệ, vì vậy nó có thể sẽ trả về một lỗi.

Cấu [[ ... ]]trúc được giới thiệu bởi vỏ Korn có lẽ vào khoảng năm 1988 vì ksh86anăm 1987 không có nó trong khi ksh88đã có nó từ đầu.

Bên cạnh ksh (tất cả các triển khai), [[...]]cũng được hỗ trợ bởi bash (kể từ phiên bản 2.02) và zsh, nhưng cả ba triển khai đều khác nhau và có sự khác biệt giữa mỗi phiên bản của cùng một vỏ mặc dù các thay đổi thường tương thích ngược (một ngoại lệ đáng chú ý là bash =~toán tử đã được biết là phá vỡ một vài tập lệnh sau một phiên bản nhất định khi hành vi của nó thay đổi). [[...]]không được chỉ định bởi POSIX hoặc Unix hoặc Linux (LSB). Nó đã được xem xét để đưa vào một vài lần, nhưng không được bao gồm vì chức năng phổ biến của nó được hỗ trợ bởi các shell chính đã được bao phủ bởi [lệnh và case-in-esaccấu trúc.

Toàn bộ [[ ... ]]cấu trúc tạo nên một lệnh. Nghĩa là, nó có trạng thái thoát (là tài sản quan trọng nhất của nó vì nó là kết quả của việc đánh giá biểu thức điều kiện), bạn có thể chuyển nó sang một lệnh khác (mặc dù nó sẽ không hữu ích) và thường sử dụng nó ở bất cứ đâu bạn muốn sử dụng bất kỳ lệnh nào khác (chỉ bên trong shell, vì nó là cấu trúc shell) nhưng nó không được phân tích cú pháp như một lệnh đơn giản thông thường. Những gì bên trong được phân tách bởi shell như một biểu thức có điều kiện và các quy tắc thông thường về phân tách từ và tạo tên tệp áp dụng khác nhau.

[[ ... ]]không biết về ==từ đầu và tương đương với =1 . Một sai lầm của ksh's (và đang gây nhầm lẫn và nhiều lỗi) là ===không phải là toán tử đẳng thức mà là toán tử khớp mẫu (mặc dù khía cạnh khớp có thể bị vô hiệu hóa khi trích dẫn nhưng với các quy tắc không rõ ràng khác với shell).

Trong mã của bạn ở trên [[ $a == z* ]], hệ vỏ sẽ phân tích thành nhiều mã thông báo theo quy tắc tương tự như quy tắc thông thường, nhận ra nó là so sánh khớp mẫu, xử lý z*như một mẫu để khớp với nội dung của abiến.

Nói chung, việc bắn vào chân bạn khó [[ ... ]]hơn so với [lệnh. Nhưng một vài quy tắc như

  • luôn luôn báo giá
  • không bao giờ sử dụng -ahoặc -ovận hành (sử dụng một số [lệnh và &&|| vỏ khai thác)

Làm cho [đáng tin cậy với vỏ POSIX.

[[...]]trong các shell khác nhau hỗ trợ các toán tử bổ sung như -nt, các toán tử khớp với biểu thức chính quy ... nhưng danh sách và hành vi thay đổi từ shell sang shell và phiên bản sang phiên bản.

Vì vậy, trừ khi bạn biết phiên bản vỏ và phiên bản tối thiểu của tập lệnh của bạn sẽ được giải thích bằng cách nào, có lẽ sẽ an toàn hơn khi sử dụng [lệnh tiêu chuẩn .


1 Một ngoại lệ: [[...]]đã được thêm vào bash trong phiên bản 2.02. Cho đến 2.03khi nó được thay đổi, [[ x = '?' ]]sẽ trả về true trong khi [[ x == '?' ]]trả về false. Đó là trích dẫn không ngăn khớp mẫu khi sử dụng =toán tử trong các phiên bản đó, nhưng đã làm khi sử dụng ==.


Thông tin tuyệt vời. Bạn có thể giải thích tại sao "không bao giờ sử dụng toán tử -a hoặc -o" không?
grebneke

@grebneke, hãy thử [ '!' = foo -o a = a ]trong bashví dụ.
Stéphane Chazelas

0

cả hai đều được sử dụng để đánh giá biểu thức và [[sẽ không hoạt động với vỏ bẻ khóa cũ hơn POSIX và [[cũng hỗ trợ khớp mẫu và biểu thức chính quy. ví dụ thử những

[ $n -eq 0 -a $y -eq 0 ] && echo "Error" || echo "Ok"

[[ $n -eq 0 && $y -eq 0 ]] && echo "Error" || echo "Ok"


Lưu ý rằng shell Bourne không phải là POSIX, chỉ [[...]]hỗ trợ regexps trong một số phiên bản của một số shell, giống như một số phiên bản [hỗ trợ kết hợp regrec. Để so sánh số học, trong các vỏ hỗ trợ [[, bạn muốn viết(( n == 0 && y == 0))
Stéphane Chazelas
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.