Tại sao một khoảng trống bắt buộc giữa [[và và-xxx xxx trong ksh?


9

Chẳng hạn, lệnh sau không hoạt động:

if [[-e xyz]]; then echo File exists;fi

ksh đưa ra lỗi sau

[[-e: command not found

Có phải vì "[[-" không rõ ràng?


2
[[là một từ khóa - có lẽ là cây phân tích của shell yêu cầu các từ khóa phải được phân định bằng khoảng trắng
Steeldo

Một cách khác để xem xét có lẽ là bạn có thể viết một hàm [[-, làm thế nào shell sẽ biết phân tích "làm cờ e trên [[" thay vì "thực hiện chức năng [[- trên e".
pbhj

Câu trả lời:


15

Lời giải thích đơn giản nhất sẽ là, bởi vì trong cuốn hướng dẫn nó xuất hiện như [[ expression ]]vậy phải có khoảng trống giữa [[expressionvà bế mạc ]]. Nhưng tất nhiên chúng ta có thể cố gắng nhìn vào nó sâu hơn. Shell có ngữ pháp hơi phức tạp và phụ thuộc rất nhiều vào khái niệm "tách từ". Cụ thể, ksh (1) trạng thái thủ công:

Shell bắt đầu phân tích cú pháp đầu vào của nó bằng cách chia nó thành các từ. Các từ, là chuỗi các ký tự, được phân cách bằng các ký tự khoảng trắng không được trích dẫn (dấu cách, tab và dòng mới) hoặc ký tự meta (<,>, |,;, &, (và)). Ngoài việc phân định các từ, khoảng trắng và tab bị bỏ qua, trong khi các dòng mới thường phân định các lệnh.

Vì vậy, như đã nêu trong hướng dẫn, chuỗi ký tự [[-eđược coi là một từ vỏ. kshsẽ tìm kiếm lệnh đó trong danh sách các toán tử tích hợp và toán tử đặc biệt (như forhoặc while), sau đó tìm lệnh bên ngoài - và voilà - không tìm thấy, do đó không tìm thấy thông báo lỗi về lệnh.

Trong trường hợp này [[cũng không phải là siêu ký tự đặc biệt. Nếu có, -ephần trong [[-eđó sẽ được coi là một từ vỏ, không phải là một đối số với [[chính nó. Tuy nhiên, [[được mô tả là lệnh ghép và hướng dẫn sử dụng cho biết như sau:

Các lệnh ghép được tạo bằng các từ dành riêng sau - những từ này chỉ được nhận ra nếu chúng không được trích dẫn và nếu chúng được sử dụng làm từ đầu tiên của lệnh (nghĩa là chúng không thể được đặt trước bởi các phép gán tham số hoặc chuyển hướng):

Vì vậy, [[để được công nhận là lệnh ghép, nó phải là từ đầu tiên của danh sách lệnh hoặc danh sách lệnh, theo định nghĩa trước đó của "từ" ngụ ý nó phải được phân tách bằng khoảng trắng, tab hoặc dòng mới từ khác từ / lập luận.


Bạn đã hỏi trong các nhận xét : "Tại sao trình phân tích cú pháp không thể dừng ngay khi tìm thấy mẫu" [["và coi đó là khởi đầu của một lệnh có điều kiện?" Câu trả lời ngắn có lẽ là do 1) ảnh hưởng của [cú pháp vì ban đầu nó là một lệnh bên ngoài và để tuân thủ tiêu chuẩn POSIX tồn tại dưới dạng lệnh bên ngoài ngay cả ngày nay và 2) vì trình phân tích cú pháp shell được xây dựng như vậy. Trình phân tích cú pháp Shell có thể nhận ra các ký tự đặc biệt được phân tách không phải dấu cách khác: echo $((2+2))(echo foobar)hoạt động hoàn toàn tốt. Có thể trong tương lai khi kshsự phát triển tiếp tục hoặc dường như có một ngã ba hoặc nhân bản (như mkshhoặc pdksh) ai đó sẽ thực hiện cú pháp không gian trống trong [[-e.

Xem thêm:


3
Tôi nghĩ rằng báo giá hướng dẫn sử dụng ksh thực sự là tại chỗ. Nó tương tự như bất kỳ ngôn ngữ lập trình nào khác: Trong C, charlà một từ khóa nhưng bạn vẫn không thể viết charsomeletter = 'A';và mong đợi trình phân tích cú pháp dừng lại sau khi nhìn thấy char.
Peter - Tái lập Monica

Tôi nghĩ rằng sự khác biệt chính giữa ví dụ của bạn về "char" và ký hiệu "[[" trong câu hỏi là, một định danh chữ và số, nói chung, cần phân cách không gian để tách biệt với các ký tự khác; mặt khác, các biểu tượng đặc biệt là mã thông báo, không cần dấu phân cách không gian.
AcBap

Chính xác. Tôi không chắc việc so sánh với C là hữu ích. Trong C người ta có thể nói i--<=++j, và trình biên dịch không có vấn đề phân tích cú pháp như i -- <= ++ j.
Scott

@Scott Tôi nghĩ rằng so sánh C là hữu ích (và đó là loại thứ cấp mà định danh C chỉ cho phép chữ và số _). Ksh có (một toán tử(echo hello)phân tích cú pháp như ( echo hello ). Nó đã if, {, [[, và các từ khóa , và ifx, {x[[xlà mỗi một chiếc thẻ - như thế nào charsomeletterlà một C token. Ngược lại, một (xmã thông báo trong ksh là hai mã thông báo - giống như i--hai mã thông báo trong C. Điều gì về mặt khái niệm thậm chí có nghĩa là một toán tử khác nhau giữa các vỏ kiểu Bourne (như ksh) và C. Nhưng Peter A. Schneider đã chỉ ra một tương tự từ vựng thực sự.
Eliah Kagan

2

Điều quan trọng cần lưu ý là đó [là một lệnh và từ khóa shell [[trong bash và ksh được dựa trên [.

[[được sử dụng theo cách tương tự [. Đôi khi bạn thậm chí có thể thay thế [ ]với [[ ]]không có thay đổi trong hành vi. Với [, giống như bất kỳ lệnh nào, một khoảng trắng là bắt buộc giữa lệnh và đối số của nó. Áp dụng tương tự cho [[.

Chúng tôi đã từng có /usr/bin/[. Bây giờ hầu hết các shell đã tích [hợp sẵn để đạt hiệu quả, nhưng cú pháp là như nhau. Trong shell cung cấp [[, nó hoạt động như một sự thay thế linh hoạt hơn[ .

Dưới đây là mô tả cho [trong bash:

$ help [
[: [ arg... ]
    Evaluate conditional expression.

    This is a synonym for the "test" builtin, but the last argument must
    be a literal `]', to match the opening `['.

Vì vậy, [tương đương với test(ngoài việc mong đợi một ]cuộc tranh luận ở cuối). help testsẽ cung cấp thêm chi tiết về nó. Bạn có thể so sánh điều này với help [[.

Ngoài ra còn có một trang man cho lệnh bên ngoài [( man \[).

Trong trường hợp

if [[-e
  • Không [phải [[là một lệnh, ở đó. Từ đầy đủ [[-elà.
  • Làm if [[-echo nó một thử nghiệm cho đúng / sai. Vì vậy, một lệnh [[-etồn tại?

Có phải vì "[[-" không rõ ràng?

Đúng. Hay không. [[-elà những gì nó là: không có gì mà shell hiểu vì vậy nó giả sử nó là lệnh riêng của nó. ;-)


"% Help [[" nói rằng "[[" là một lệnh có điều kiện. Tại sao trình phân tích cú pháp không thể dừng ngay khi tìm thấy mẫu "[[" và coi đó là khởi đầu của một lệnh có điều kiện?
AcBap

@Myloti [[-elà một tên lệnh có khả năng hợp lệ (nếu trông lạ).
Gordon Davisson

2

Không gian là một dấu phân cách và được yêu cầu. Như bạn có thể thấy từ shellcheck:

$ shellcheck gmail-browse-msgs-algorithm.sh

In gmail-browse-msgs-algorithm.sh line 847:
        [[$Today != "${DaysArr[ i + DAY_DELETED_ON_NDX ]}" ]] && continue
          ^-- SC1035: You need a space after the [[ and before the ]].

(Cả hỗ trợ ksh và bash [[và không hoạt động mà không có khoảng trống. Shellcheck cung cấp chính xác đầu ra đó với các tập lệnh ksh và bash tương tự có chứa dòng lỗi đó.)

Tại sao cần phân định phải liên quan đến mã thông báo và từ vựng .


Xin lưu ý, OP đã hỏi về kshvỏ trong câu hỏi. Bash mượn [[toán tử từ kshvà trong câu hỏi này hành vi của họ là giống hệt nhau, nhưng tôi sẽ thận trọng với các giả định vì có một số khác biệt đáng kể giữa kshbashhành vi và nội bộ. Đó chỉ là vì shellcheck hoạt động trên tập lệnh bash, nó có thể không nhất thiết là công cụ thích hợp cho tập lệnh ksh. Chỉ cần một cái gì đó để ghi nhớ trong khi tiếp cận các vỏ khác nhau
Sergiy Kolodyazhnyy

@SergiyKolodyazhnyy Tôi đã có cơ hội sử dụng shellcheck để làm nổi bật các [[quy tắc tích hợp. Tôi không có cách nào suy ra đó kshlà một cái vỏ shellcheckcó thể tìm thấy lỗi. Tôi hy vọng người khác đánh giá cao kshbashlà hai phiên dịch viên khác nhau. Cảm ơn bạn đã đề cập đến điều đó. Cũng đáng chú ý [[là một bash được xây dựng trong khi [là một lệnh bên ngoài.
WinEunuuchs2Unix

Trên thực tế [cũng là một bash tích hợp :) manpages.ubfox.com/manpages/bionic/man7/bash-builtins.7.html Nhưng bạn không ở xa - [hoặc testđược yêu cầu phải là một lệnh bên ngoài. Vào thời của vỏ Bourne ban đầu [trên thực tế là một lệnh bên ngoài và ngày nay vẫn tồn tại /usr/bin/[vì POSIX yêu cầu nó phải là một lệnh bên ngoài pubs.opengroup.org/onlinepub/009695399/utilities/test.html Rất ít được POSIX yêu cầu được xây dựng-in pubs.opengroup.org/onlinepubs/009695399/idx/sbi.html Builtin [là cho hiệu quả
Sergiy Kolodyazhnyy

2
Shellcheck biết ksh; sử dụng một hashbang hoặc -s ksh. Btw, ksh và bash đã [["tích hợp sẵn", nhưng đó là một từ khóa , không giống như [, đó là một phần mềm dựng sẵn . (ksh : whence -v [ [[; bash type [ [[:) Các nội trang và các lệnh bên ngoài giống nhau về mặt cú pháp. Một cách [[khác [[[ngăn chặn một số mở rộng trong các đối số của nó, điều mà nó không thể là một nội dung - nhiều như {không thể phân nhóm được nó là một nội dung. Đây có thể là lý do tại sao {x(hoặc [[x) là một mã thông báo cảm thấy kỳ lạ. Tôi nghĩ thật đúng khi nói nội dung và từ khóa là từ vựng nhưng không giống nhau về mặt cú pháp . @SergiyKolodyazhnyy
Eliah Kagan

1
@EliahKagan Trên thực tế, tôi đã đăng câu hỏi trên U & L để có câu trả lời chính xác về những gì POSIX nghĩ về tình huống này unix.stackexchange.com/questions/526574/ ngôn ngữ Shell Shell đủ phức tạp để hy vọng ai đó có thể giải thích điều đó một cách chính thức hơn
Sergiy Kolodyazhnyy

1

[[có thể là một lệnh bên ngoài! Đó là, nó có thể là một chương trình và không phải là một số cú pháp được hỗ trợ trực tiếp bởi shell của bạn. Có thể hỗ trợ cú pháp không gian trống kshnhưng nó sẽ thất bại trên các hệ thống có bên ngoài [[vì vậy vì lý do tương thích, tốt hơn là giữ không gian cần thiết.

BusyBox đang cung cấp [[như một lệnh bên ngoài , ví dụ.


0

[[-ecó thể tham gia mở rộng vỏ (nó có thể được sử dụng như

echo [[-e]*

để liệt kê tất cả các tệp bắt đầu bằng một chữ cái giữa [ebao gồm), nó sẽ là một mớ hỗn độn nếu []là các ký tự đặc biệt không tham gia vào phân tách từ được điều chỉnh bởi khoảng trắng thông thường.

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.