Nhiều toán tử logic, ((A || B) && C) và lỗi cú pháp gần mã thông báo không mong muốn


24

Tôi đang làm việc với Bash 3 và tôi đang cố gắng tạo thành một điều kiện. Trong C / C ++, đơn giản chết của nó : ((A || B) && C). Ở Bash, hóa ra không phải vậy (tôi nghĩ rằng các tác giả Git phải đóng góp mã này trước khi họ chuyển sang các nỗ lực khác).

Điều này không hoạt động. Lưu ý rằng đó <0 or 1>không phải là một chuỗi ký tự; nó có nghĩa là 0 hoặc 1 (thường xuất phát từ grep -i).

A=<0 or 1>
B=<0 or 1>
C=<0 or 1>
if [ [ "$A" -eq "0" ] || [ "$B" -ne "0" ] ] && [ "$C" -eqe "0" ]; then ... fi

Nó dẫn đến:

line 322: syntax error near unexpected token `[['

Sau đó tôi đã thử:

A=<0 or 1>
B=<0 or 1>
C=<0 or 1>
if [ ([ "$A" -eq "0" ]) || ([ "$B" -ne "0" ]) ] && [ "$C" -eq "0" ]; then ... fi

nó dẫn đến:

line 322: syntax error near unexpected token `[['

Một phần của vấn đề là kết quả tìm kiếm là những ví dụ tầm thường, và không phải là những ví dụ phức tạp hơn với các điều kiện ghép.

Làm thế nào để tôi thực hiện một đơn giản ((A || B) && C)trong Bash?


Tôi đã sẵn sàng để hủy đăng ký nó và lặp lại các lệnh tương tự trong nhiều khối:

A=<0 or 1>
B=<0 or 1>
C=<0 or 1>

if [ "$A" -eq "0" ] && [ "$C" -eq "0" ]; then
    ...
elif [ "$B" -ne "0" ] && [ "$C" -eq "0" ]; then
    ... 
fi

Câu trả lời:


42

Cú pháp của bash không giống như C, ngay cả khi một phần nhỏ của nó được lấy cảm hứng từ C. Bạn không thể đơn giản thử viết mã C và mong đợi nó hoạt động.

Điểm chính của shell là chạy các lệnh. Lệnh khung mở [là một lệnh, thực hiện một thử nghiệm duy nhất¹. Bạn thậm chí có thể viết nó dưới dạng test(không có khung đóng cuối cùng). Các toán tử ||&&toán tử là các toán tử shell, chúng kết hợp các lệnh , không phải kiểm tra.

Vì vậy, khi bạn viết

[ [ "$A" -eq "0" ] || [ "$B" -ne "0" ] ] && [ "$C" -eq "0" ]

đó là phân tích cú pháp như

[ [ "$A" -eq "0" ] ||
[ "$B" -ne "0" ] ] &&
[ "$C" -eq "0" ]

giống như

test [ "$A" -eq "0" ||
test "$B" -ne "0" ] &&
test "$C" -eq "0"

Chú ý dấu ngoặc không cân bằng? Vâng, điều đó không tốt. Nỗ lực của bạn với dấu ngoặc đơn có cùng một vấn đề: dấu ngoặc giả.

Cú pháp để nhóm các lệnh với nhau là dấu ngoặc nhọn. Cách niềng răng được phân tích cú pháp đòi hỏi một lệnh hoàn chỉnh trước chúng, vì vậy bạn sẽ cần chấm dứt lệnh bên trong dấu ngoặc nhọn bằng một dòng mới hoặc dấu chấm phẩy.

if { [ "$A" -eq "0" ] || [ "$B" -ne "0" ]; } && [ "$C" -eq "0" ]; then 

Có một cách khác là sử dụng dấu ngoặc kép. Không giống như dấu ngoặc đơn, dấu ngoặc kép là cú pháp shell đặc biệt. Họ phân định biểu thức điều kiện . Trong dấu ngoặc kép, bạn có thể sử dụng dấu ngoặc đơn và toán tử như &&||. Vì dấu ngoặc kép là cú pháp shell, nên shell biết rằng khi các toán tử này nằm trong dấu ngoặc, chúng là một phần của cú pháp biểu thức điều kiện, không phải là một phần của cú pháp lệnh shell thông thường.

if [[ ($A -eq 0 || $B -ne 0) && $C -eq 0 ]]; then 

Nếu tất cả các bài kiểm tra của bạn là số, thì vẫn còn một cách khác, đó là phân định các biểu thức số học . Biểu thức số học thực hiện tính toán số nguyên với cú pháp rất giống C.

if (((A == 0 || B != 0) && C == 0)); then 

Bạn có thể thấy mồi bash ngoặc của tôi hữu ích.

[có thể được sử dụng trong đồng bằng sh. [[((đặc trưng cho bash (và ksh và zsh).

¹ Nó cũng có thể kết hợp nhiều thử nghiệm với các toán tử logic, nhưng đây là rườm rà để sử dụng và có những cạm bẫy tinh tế vì vậy tôi sẽ không giải thích nó.


Cảm ơn Giles. Điều này có giữ cho Bash 2 đến Bash 4 không? Tôi cần một cái gì đó nhẹ nhàng di động, nhưng Kusalananda đã đề cập đến một số điều tôi không làm là di động (điều đó có nghĩa là những gì tôi đang làm không phải là di động?). Ở đây, nhẹ có nghĩa là Bash 2 đến Bash 4.

@jww [[ … ]]đã được thêm vào bash 2.02. ((…))đã được thêm vào bash 2.0.
Gilles 'SO- ngừng trở nên xấu xa'

@Giles - cảm ơn. Bash 2 có lẽ là đáng cười nhất. Đó là do quản trị của chúng tôi, và không sẵn sàng từ bỏ các nền tảng cũ hơn. Bash 2 và GCC 3 xuất hiện trong thử nghiệm Fedora 1 của chúng tôi. Chúng tôi sẽ để một nền tảng cũ đi vào dịp này, nhưng phải có lý do cho nó. Chúng tôi không đăng ký chính sách từ bỏ của Apple, Microsoft, Mozilla, v.v.

@jww Hãy hiểu rằng sự ưu tiên ||&&thay đổi từ [sang [[((. Bình đẳng ưu tiên trong [(sử dụng dấu ngoặc đơn để kiểm soát trình tự đánh giá), nhưng && có độ ưu tiên cao trong [[((hơn ||(như trong C).

6

Sử dụng [[:

if [[ ( "$A" -eq "0" || "$B" -ne "0" ) && "$C" -eq "0" ]]; then ...

Nếu bạn thích [, các công việc sau:

if [ \( "$A" -eq "0" -o "$B" -ne "0" \) -a "$C" -eq "0" ]; then ...

3

Sử dụng -otoán tử thay vì lồng nhau | |. Bạn cũng có thể sử dụng -ađể thay thế && nếu cần trong các báo cáo khác của mình.

   EXPRESSION1 -a EXPRESSION2
          both EXPRESSION1 and EXPRESSION2 are true

   EXPRESSION1 -o EXPRESSION2
          either EXPRESSION1 or EXPRESSION2 is true


if [  "$A" -eq "0" -o "$B" -ne "0"  ] && [ "$C" -eq "0" ]; then echo success;fi

Cảm ơn. Tôi đã đi qua -a-o, nhưng tôi đã không thử nghiệm với họ. Ai đó trên Stack Overflow nói để tránh nó. Tôi hầu hết đồng ý với họ vì kịch bản ít đọc hơn khi sử dụng chúng.

... nhưng di động hơn.
Kusalananda

@Kusalananda - Cảm ơn bạn đã ủng hộ tính di động. Kịch bản phải chạy trên các hệ thống có Bash 2 đến Bash 4. Sẽ [[ ( "$A" -eq "0" || "$B" -ne "0" ) && "$C" -eq "0" ]]gặp rắc rối với chúng?

Sẽ không có vấn đề gì miễn là bạn giữ bashhoặc bất kỳ vỏ nào khác hiểu được [[ ... ]].
Kusalananda
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.