Tại sao `|` được xử lý theo nghĩa đen trong mô hình toàn cầu?


13

Câu hỏi của tôi đến từ Làm thế nào để lưu trữ biểu thức chính quy trong biến shell tránh các vấn đề với trích dẫn các ký tự đặc biệt cho trình bao? .

  1. Tại sao có lỗi:

    $ [[ $a = a|b ]]  
    bash: syntax error in conditional expression: unexpected token `|'
    bash: syntax error near `|b'

    Bên trong [[ ... ]]toán hạng thứ hai =dự kiến ​​sẽ là một mô hình toàn cầu.

    a|b một mẫu hình cầu hợp lệ? Bạn có thể chỉ ra quy tắc cú pháp nào nó vi phạm?

  2. Một số bình luận dưới đây chỉ ra rằng | được hiểu là ống.

    Sau đó thay đổi =cho mẫu toàn cầu thành mẫu =~regex làm cho |công việc

    $ [[ $a =~ a|b ]]

    Tôi đã học được từ Học Bash P180 trong bài trước của tôi|được công nhận là ống vào đầu giải thích, thậm chí trước khi bất kỳ bước khác giải thích (bao gồm phân tích các biểu thức điều kiện trong các ví dụ). Vậy làm thế nào để có thể |được công nhận là toán tử regex khi sử dụng =~, mà không được công nhận là đường ống trong sử dụng không hợp lệ, giống như khi sử dụng =? Điều đó khiến tôi nghĩ rằng lỗi cú pháp trong phần 1 không có nghĩa |là được hiểu là một đường ống.

    Mỗi dòng mà shell đọc từ đầu vào tiêu chuẩn hoặc tập lệnh được gọi là đường ống; nó chứa một hoặc nhiều lệnh được phân tách bằng 0 hoặc nhiều ký tự ống (|). Đối với mỗi đường ống mà nó đọc, shell sẽ chia nó thành các lệnh, thiết lập I / O cho đường ống, sau đó thực hiện các thao tác sau cho mỗi lệnh (Hình 7-1):

Cảm ơn.


1
Lưu ý rằng trong một số phiên bản bash, phân tích cú pháp extglob ( |đặ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à shoptcấ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ơi extglobđang bật, theo mặc định hoặc cấu hình rõ ràng, thì chúng ta đang ở đó.
Charles Duffy

2
BTW, nếu bạn muốn loại trừ một cách toàn diện hơn trường hợp nhân vật tẩu can thiệp vào giai đoạn phân tích cú pháp trước đó (mà tôi đồng ý là không xảy ra, nhưng nó không rõ ràng với người đọc như vậy), bạn sử dụng pattern='a|b'và sau đó mở rộng không $patterntrích dẫn trên RHS.
Charles Duffy

@CharlesDuffy, đó là điểm được đưa ra trong Hỏi & Đáp mà câu hỏi này là câu hỏi tiếp theo.
Stéphane Chazelas

Ahh - bối cảnh có ý nghĩa; và câu trả lời của bạn ở đây là nổi bật. Cảm ơn bạn về cả hai tính.
Charles Duffy

Tim, dijd bất kỳ câu trả lời dưới đây trả lời câu hỏi của bạn? Hãy xem xét chấp nhận một nếu như vậy. Cảm ơn bạn!
Jeff Schaller

Câu trả lời:


13

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|bchuỗ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í đó , bashmong đợ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ể extglobtù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 adẫn đến b. a|bkhô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 ]], bashmong đợ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ị, bashkhông phàn nàn trong:

[[ $a = a||b ]]

Bởi vì bây giờ nó là amã 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 đó $aahoặc bchuỗi không trống.

Bây giờ, trong:

[[ $a =~ a|b ]]

bashkhô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|blà 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|bregrec mà chống lại a\|bregrec. Đó 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, bashphả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 bashsẽ 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ì bashkhô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 ], \).

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|bregrec.

Đ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 zshnhư =somethinglà 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ù bashlà vỏ đầu tiên xuất hiện [[ =~ ]]), nhưng bạn vẫn có thể thực hiện BASH_COMPAT=31hoặc shopt -s compat31hoàn nguyên hành vi trước đó (ngoại trừ việc [[ $a =~ a|b ]]sẽ trả về lỗi trong bash3.1, nhưng nó không còn nữa trong bash -O compat31vớ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.


zsh cũng đang báo cáo lỗi [[ $a = a|b ]].
Isaac

@isaac, vâng, đó là điểm tôi đang thực hiện ở đây. a|bkhông phải là một vỏ WORD ở đây, đó là a, |btượng trưng. Giống như echo a|bkhông đầu ra a|bhoặc không mở rộng toàn a|bcầu, bạn cần trích dẫn rằng |đó là một ký tự vỏ đặc biệt không hợp lệ trong bối cảnh đó. [[ $a = (a|b) ]]sẽ hoạt động như echo (a|b)sẽ làm việc như (a|b)là một toán tử ký tự đại diện zsh.
Stéphane Chazelas

Các từ ngữ và giải thích về câu trả lời của bạn chỉ tên bash. Đó không phải là toàn bộ sự thật.
Isaac

11

Globs chuẩn ( "filename mở rộng") là: *, ?, và [ ... ]. |không phải là toán tử toàn cầu hợp lệ trong cài đặt tiêu chuẩn (không phải extglob).

Thử:

shopt -s extglob
[[ a = @(a|b) ]] && echo matched

1
Cảm ơn. Nhưng tại sao không được |hiểu theo nghĩa đen? Tại sao có lỗi cú pháp?
Tim

1
Nó không được trích dẫn.
Jeff Schaller

3
Trong cài đặt tiêu chuẩn, |không phải là nhà điều hành toàn cầu, vì vậy không được |giải thích theo nghĩa đen mà không được trích dẫn? Vậy tại sao có lỗi cú pháp?
Tim

1
|là một nhân vật điều khiển; nó không bao giờ được coi là một ký tự theo nghĩa đen giống như một chữ cái hoặc số.
chepner

3
Bởi vì trong chế độ đó, shell không mong đợi một ký tự chuyển hướng ống ở giữa một [chưa] đóng [[]]. [[ $a = akhông phải là một lệnh hợp lệ mà đầu ra của nó có thể được dẫn đến một quá trình khác (ít nhất đó là điều mà shell nghĩ rằng bạn đang cố gắng thực hiện).
Jason C

5

Nếu bạn muốn một trận đấu regex, bài kiểm tra sẽ là:

[[ "$a" =~ a|b ]]

@Tim Bạn nên mở câu hỏi mới, không liên tục chỉnh sửa câu hỏi hiện tại của bạn.
vườn

@gardenhead: Cập nhật của tôi là để làm rõ các câu hỏi của tôi, thay vì thay đổi chúng, trong trường hợp bạn bỏ lỡ nó. Phần thứ hai tôi đã thêm là để hiển thị một lời giải thích đường ống của câu hỏi về câu hỏi ban đầu của tôi (tại sao lỗi cú pháp) là không chính xác.
Tim
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.