Lỗi trong kiểm tra khung vỏ khi chuỗi là dấu ngoặc trái


27

Tôi đã từng tự tin về thực tế rằng trích dẫn các chuỗi luôn luôn là một thực hành tốt để tránh việc trình bao phân tách nó.

Sau đó, tôi đã đi qua điều này:

$ x='('
$ [ "$x" = '1' -a "$y" = '1' ]
bash: [: `)' expected, found 1

Cố gắng cô lập vấn đề, nhận được cùng một lỗi:

$ [ '(' = '1' -a '1' = '1' ]
bash: [: `)' expected, found 1

Tôi đã giải quyết vấn đề như thế này:

[ "$x" = '1' ] && [ "$y" = '1' ]

Tôi vẫn cần phải biết những gì đang xảy ra ở đây.


2
Như một cách giải quyết, trong bash, bạn có thể sử dụng[[ "$x" = '1' && "$y" = '1' ]]
terdon

3
Đặc tả POSIX để kiểm tra mô tả rõ ràng -a-olà lỗi thời vì lý do này (đó là ý nghĩa của [OB]siêu ký tự bên cạnh đặc điểm kỹ thuật của chúng). Nếu bạn đã viết [ "$x" = 1 ] && [ "$y" = 1 ]thay thế, bạn sẽ ổn, và sẽ ổn trong phạm vi của hành vi được xác định rõ / chuẩn hóa.
Charles Duffy

6
Đây là lý do tại sao mọi người thường sử dụng [ "x$x" = "x1" ]để ngăn chặn các đối số bị hiểu sai là toán tử.
Jonathan Leffler

@JonathanLeffler: Này, bạn cô đọng câu trả lời của tôi vào một câu duy nhất, không công bằng! :) Nếu một người sử dụng shell POSIX như dashthay vì Bash, thì đây vẫn là một cách thực hành hữu ích.
Động vật danh nghĩa

Tôi muốn cảm ơn tất cả mọi người đã dành thời gian để trả lời câu hỏi của tôi, những người thực sự đánh giá cao :)! Tôi cũng muốn cảm ơn vì đã chỉnh sửa quan trọng cho câu hỏi của tôi. Vào diễn đàn này đôi khi mang lại cho tôi cảm giác hồi hộp tương tự khi thoát khỏi lớp Methraz, di chuyển sai có nghĩa là cuộc sống của bạn. Dù sao, tôi thực sự mong lời cảm ơn của tôi sẽ đến với bạn trước khi bình luận này bị xóa
Claudio

Câu trả lời:


25

Đây là một trường hợp góc rất khó hiểu mà người ta có thể xem xét một lỗi trong cách [xác định tích hợp kiểm tra ; tuy nhiên, nó phù hợp với hành vi của [nhị phân thực tế có sẵn trên nhiều hệ thống. Theo như tôi có thể nói, nó chỉ ảnh hưởng đến một số trường hợp và một biến có giá trị phù hợp với một [nhà khai thác như (, !, =, -e, và vân vân.

Hãy để tôi giải thích lý do tại sao và cách xử lý xung quanh nó trong shell Bash và POSIX.


Giải trình:

Hãy xem xét những điều sau đây:

x="("
[ "$x" = "(" ] && echo yes || echo no

Không vấn đề gì; các kết quả trên không có lỗi, và đầu ra yes. Đây là cách chúng tôi mong đợi công cụ để làm việc. Bạn có thể thay đổi chuỗi so sánh thành '1'nếu bạn thích và giá trị của xnó và nó sẽ hoạt động như mong đợi.

Lưu ý rằng /usr/bin/[nhị phân thực tế hoạt động theo cùng một cách. Nếu bạn chạy, ví dụ như '/usr/bin/[' '(' = '(' ']'không có lỗi, bởi vì chương trình có thể phát hiện ra rằng các đối số bao gồm một hoạt động so sánh chuỗi đơn.

Lỗi xảy ra khi chúng ta với một biểu thức thứ hai. Không quan trọng biểu thức thứ hai là gì, miễn là nó hợp lệ. Ví dụ,

[ '1' = '1' ] && echo yes || echo no

đầu ra yes, và rõ ràng là một biểu thức hợp lệ; nhưng, nếu chúng ta kết hợp cả hai,

[ "$x" = "(" -a '1' = '1' ] && echo yes || echo no

Bash từ chối biểu thức khi và chỉ khi x(hoặc !.

Nếu chúng ta chạy chương trình trên bằng [chương trình thực tế , tức là

'/usr/bin/[' "$x" = "(" -a '1' = '1' ] && echo yes || echo no

lỗi sẽ hiểu: từ vỏ hiện các thay biến, /usr/bin/[nhị phân chỉ nhận được các thông số ( = ( -a 1 = 1và chấm dứt ], nó dễ hiểu thất bại trong việc phân tích xem ngoặc mở bắt đầu một tiểu biểu hiện hay không, có trở thành một hoạt động liên quan. Chắc chắn, phân tích nó như là hai so sánh chuỗi là có thể, nhưng thực hiện nó một cách tham lam như thế có thể gây ra vấn đề khi áp dụng cho các biểu thức thích hợp với các biểu thức con được ngoặc đơn.

Vấn đề, thực sự, là cái vỏ tích [hợp hoạt động theo cùng một cách, như thể nó mở rộng giá trị của xtrước khi kiểm tra biểu thức.

(Những sự mơ hồ này và những điều khác liên quan đến việc mở rộng biến đổi, là một lý do lớn khiến Bash thực hiện và hiện khuyên nên sử dụng các [[ ... ]]biểu thức kiểm tra thay thế.)


Cách giải quyết là tầm thường và thường thấy trong các tập lệnh sử dụng shshell cũ . Bạn thêm một ký tự "an toàn", thường x, ở phía trước các chuỗi (cả hai giá trị được so sánh), để đảm bảo biểu thức được nhận dạng dưới dạng so sánh chuỗi:

[ "x$x" = "x(" -a "x$y" = "x1" ]

1
Tôi sẽ không gọi hành vi của [một lỗi tích hợp. Nếu bất cứ điều gì, đó là một lỗ hổng thiết kế vốn có. [[là một từ khóa shell , không chỉ là một lệnh tích hợp, vì vậy nó phải xem xét mọi thứ trước khi loại bỏ trích dẫn và thực sự ghi đè lên việc tách từ thông thường. vd: [[ $x == 1 ]]không cần sử dụng "$x", vì [[ngữ cảnh khác với bình thường. Dù sao, đây là cách [[có thể tránh những cạm bẫy [. POSIX yêu cầu [phải hành xử theo cách của nó và bash chủ yếu tuân thủ POSIX ngay cả khi không có --posix, vì vậy việc thay đổi [thành một từ khóa là không hấp dẫn.
Peter Cordes

Cách giải quyết của bạn không được POSIX khuyến nghị. Chỉ cần sử dụng hai cuộc gọi đến [.
tự đại diện

@PeterCordes: Hoàn toàn đúng; điểm tốt. (Có lẽ tôi nên sử dụng được thiết kế thay vì được định nghĩa trong đoạn đầu tiên của mình, nhưng [hành vi của nó bị gánh nặng bởi lịch sử trước POSIX, tôi đã chọn từ sau thay thế.) Cá nhân tôi tránh sử dụng [[trong các tập lệnh ví dụ của mình, nhưng chỉ vì đó là một ngoại trừ đề xuất trích dẫn mà tôi luôn biết về (vì bỏ qua các trích dẫn là lý do phổ biến nhất cho các lỗi tập lệnh mà tôi thấy) và tôi đã không nghĩ đến một đoạn đơn giản để giải thích tại sao lại [[là một ngoại lệ đối với các quy tắc, mà không đưa ra khuyến nghị trích dẫn của tôi nghi ngờ.
Động vật danh nghĩa

@Wildcard: Không. POSIX không khuyến nghị thực hành này và đó là điều quan trọng. Chỉ vì một cơ quan không xảy ra để khuyến nghị thực hành này, không làm cho điều này trở nên tồi tệ. Thật vậy, như tôi chỉ ra, đây là thực tiễn lịch sử được sử dụng trong shcác tập lệnh, trước khi chuẩn hóa POSIX. Sử dụng các biểu thức con được ngoặc đơn -a-ocác toán tử logic trong các bài kiểm tra / [hiệu quả hơn dựa vào chuỗi biểu thức (thông qua &&||); Chỉ là trên các máy hiện tại, sự khác biệt là không liên quan.
Động vật danh nghĩa

@Wildcard: Tuy nhiên, cá nhân tôi thích xâu chuỗi các biểu thức kiểm tra bằng cách sử dụng &&||, nhưng lý do là, nó giúp con người chúng ta dễ dàng duy trì (đọc, hiểu và sửa đổi nếu / khi cần thiết) mà không cần đưa ra lỗi. Vì vậy, tôi không chỉ trích đề xuất của bạn, mà chỉ là lý do đằng sau gợi ý. (Vì lý do rất giống nhau, nhưng .LT., .GE.vv toán tử so sánh trong FORTRAN 77 có các phiên bản nhiều con người thân thiện hơn <, >=vv trong các phiên bản sau này.)
danh nghĩa Animal

11

[aka testthấy:

 argc: 1 2 3 4  5 6 7 8
 argv: ( = 1 -a 1 = 1 ]

testchấp nhận biểu hiện phụ trong ngoặc đơn; Vì vậy, nó nghĩ rằng dấu ngoặc đơn bên trái mở ra một biểu thức con và đang cố phân tích nó; trình phân tích cú pháp xem =là điều đầu tiên trong biểu thức con và nghĩ rằng đó là một thử nghiệm độ dài chuỗi ẩn, vì vậy nó rất vui; biểu thức con nên được theo sau bởi dấu ngoặc đơn bên phải và thay vào đó trình phân tích cú pháp tìm thấy 1thay vì ). Và nó phàn nàn.

Khi testcó chính xác ba đối số và đối số giữa là một trong các toán tử được công nhận, nó áp dụng toán tử đó cho đối số thứ 1 và thứ 3 mà không cần tìm các biểu thức con trong ngoặc đơn.

Để biết chi tiết đầy đủ man bash, hãy tìm kiếm test expr.

Kết luận: Thuật toán phân tích cú pháp được sử dụng testlà phức tạp. Chỉ sử dụng biểu thức đơn giản và sử dụng vỏ các nhà khai thác !, &&||để kết hợp chú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.