Không có lý do chính đáng tại sao
[[ $a = a|b ]]
Nên báo cáo lỗi thay vì kiểm tra xem $ a có phải là a|b
chuỗi không, trong khi [[ $a =~ a|b ]]
không trả về lỗi.
Lý do duy nhất |
là nói chung (bên ngoài và bên trong [[ ... ]]
) một nhân vật đặc biệt. Ở [[ $a =
vị trí đó , bash
mong đợi một loại mã thông báo là một WORD bình thường như các đối số hoặc các mục tiêu chuyển hướng trong một dòng lệnh shell thông thường (nhưng như thể extglob
tùy chọn đã được bật kể từ bash 4.1).
(bởi WORD ở đây, tôi đề cập đến một từ trong ngữ pháp shell giả thuyết giống như từ được mô tả bởi đặc tả POSIX , đó là thứ mà shell sẽ phân tích thành một mã thông báo trong một dòng lệnh shell đơn giản, không phải là định nghĩa khác của các từ như tiếng Anh một trong một chuỗi các ký tự hoặc một chuỗi các ký tự không phải khoảng cách. foo"bar baz"
, $(echo x y)
là hai ví dụ WORD s).
Trong một dòng lệnh shell thông thường:
echo a|b
Được echo a
dẫn đến b
. a|b
không phải là một WORD , đó là ba thẻ: a a
WORD , một |
token và một b
WORD token.
Khi được sử dụng [[ $a = a|b ]]
, bash
mong đợi một WORD mà nó nhận được ( a
), nhưng sau đó tìm thấy một |
mã thông báo không mong muốn gây ra lỗi.
Thật thú vị, bash
không phàn nàn trong:
[[ $a = a||b ]]
Bởi vì bây giờ nó là a
mã thông báo theo sau là ||
mã thông báo b
, do đó, nó được phân tích cú pháp giống như:
[[ $a = a || b ]]
Đó là kiểm tra đó $a
là a
hoặc b
chuỗi không trống.
Bây giờ, trong:
[[ $a =~ a|b ]]
bash
không thể có cùng quy tắc phân tích cú pháp. Có cùng một quy tắc phân tích cú pháp có nghĩa là những điều trên sẽ gây ra lỗi và người ta sẽ cần trích dẫn điều đó |
để đảm bảo a|b
là một WORD duy nhất . Nhưng, kể từ bash 3.2, nếu bạn làm:
[[ $a =~ 'a|b' ]]
Điều đó không còn phù hợp với a|b
regrec mà chống lại a\|b
regrec. Đó là, trích dẫn shell có tác dụng phụ trong việc loại bỏ ý nghĩa đặc biệt của các toán tử regrec. Đây là một tính năng, do đó, hành vi tương tự như hành vi [[ $a = "?" ]]
, nhưng các mẫu ký tự đại diện (được sử dụng trong [[ $a = pattern ]]
) là các vỏ WORDS (ví dụ được sử dụng trong các khối), trong khi biểu thức chính quy thì không.
Vì vậy, bash
phải đối xử với tất cả các nhà khai thác mở rộng regexp rằng nếu không thường ký tự đặc biệt như vỏ |
, (
, )
khác nhau khi phân tích một cuộc tranh cãi của các =~
nhà điều hành.
Tuy nhiên, lưu ý rằng trong khi
[[ $a =~ (ab)*c ]]
bây giờ làm việc
[[ $a =~ [)}] ]]
không. Bạn cần:
[[ $a =~ [\)}] ]]
[[ $a =~ [')'}] ]]
Mà trong các phiên bản trước bash
sẽ khớp không chính xác trên dấu gạch chéo ngược. Cái đó đã được sửa, nhưng
[[ $a =~ [^]')'] ]]
Liệu không phù hợp trên xuyệc ngược như nó nên ví dụ. Bởi vì bash
không nhận ra rằng )
nằm trong dấu ngoặc, do đó thoát khỏi )
kết quả là một biểu [^]\)]
thức chính quy phù hợp với bất kỳ ký tự nào ]
, \
và )
.
ksh93
có nhiều lỗi tồi tệ hơn trên mặt trận đó.
Trong zsh
đó, đó là một từ vỏ bình thường được mong đợi và trích dẫn các toán tử regrec không ảnh hưởng đến ý nghĩa của các toán tử regrec.
[[ $a =~ 'a|b' ]]
Là phù hợp với a|b
regrec.
Điều đó có nghĩa là =~
cũng có thể được thêm vào lệnh [
/ test
:
[ "$a" '=~' 'a|b' ]
test "$a" '=~' 'a|b'
(cũng hoạt động trong yash
. Các =~
nhu cầu được trích dẫn zsh
như =something
là một toán tử shell đặc biệt ở đó).
bash 3.1 dùng để hành xử như thế nào zsh
. Nó đã thay đổi trong 3.2, có lẽ là để phù hợp với ksh93
(mặc dù bash
là vỏ đầu tiên xuất hiện [[ =~ ]]
), nhưng bạn vẫn có thể thực hiện BASH_COMPAT=31
hoặc shopt -s compat31
hoàn nguyên hành vi trước đó (ngoại trừ việc [[ $a =~ a|b ]]
sẽ trả về lỗi trong bash
3.1, nhưng nó không còn nữa trong bash -O compat31
với các phiên bản mới hơn của bash
).
Hy vọng nó làm rõ lý do tại sao tôi nói các quy tắc là khó hiểu và tại sao sử dụng:
[[ $a =~ $var ]]
giúp bao gồm tính di động cho các vỏ khác.
|
đặc biệt) được bật theo mặc định ở phía bên phải của[[ $var = $pattern ]]
. Sẽ rất thú vị khi cô lập các phiên bản vàshopt
cấu hình tùy chọn nơi hành vi này được nhìn thấy - nếu đó chỉ là những nơiextglob
đang bật, theo mặc định hoặc cấu hình rõ ràng, thì chúng ta đang ở đó.