Làm thế nào để biểu diễn nhiều điều kiện trong một shell if statement?


308

Tôi muốn đại diện cho nhiều điều kiện như thế này:

if [ ( $g -eq 1 -a "$c" = "123" ) -o ( $g -eq 2 -a "$c" = "456" ) ]   
then  
    echo abc;  
else  
    echo efg;   
fi  

nhưng khi tôi thực thi kịch bản, nó hiển thị

syntax error at line 15: `[' unexpected, 

trong đó dòng 15 là dòng hiển thị nếu ....

Điều gì là sai với điều kiện này? Tôi đoán có gì đó không ổn với ().


6
Bạn không hỏi về điều kiện vỏ nhưng điều kiện kiểm tra . Toàn bộ biểu thức trong ví dụ của bạn được đánh giá bởi test( [) chứ không phải bởi shell. Shell chỉ đánh giá trạng thái thoát của [.
ceving


Câu trả lời:


381

Kỹ thuật cổ điển (siêu nhân vật thoát):

if [ \( "$g" -eq 1 -a "$c" = "123" \) -o \( "$g" -eq 2 -a "$c" = "456" \) ]
then echo abc
else echo efg
fi

Tôi đã đính kèm các tài liệu tham khảo $gtrong dấu ngoặc kép; Đó là cách thực hành tốt, nói chung. Nghiêm túc, các dấu ngoặc đơn không cần thiết bởi vì quyền ưu tiên -a-olàm cho nó chính xác ngay cả khi không có chúng.

Lưu ý rằng các toán tử -a-otoán tử là một phần của đặc tả POSIX test, hay [, chủ yếu là để tương thích ngược ( teství dụ, vì chúng là một phần của UNIX phiên bản thứ 7), nhưng chúng được đánh dấu rõ ràng là 'lỗi thời' bởi POSIX. Bash (xem các biểu thức điều kiện ) dường như ưu tiên các ý nghĩa cổ điển và POSIX cho -a-ovới các toán tử thay thế riêng của nó có các đối số.


Với một số lưu ý, bạn có thể sử dụng [[toán tử hiện đại hơn , nhưng lưu ý rằng các phiên bản trong Bash và Korn Shell (chẳng hạn) không cần phải giống hệt nhau.

for g in 1 2 3
do
    for c in 123 456 789
    do
        if [[ ( "$g" -eq 1 && "$c" = "123" ) || ( "$g" -eq 2 && "$c" = "456" ) ]]
        then echo "g = $g; c = $c; true"
        else echo "g = $g; c = $c; false"
        fi
    done
done

Ví dụ chạy, sử dụng Bash 3.2.57 trên Mac OS X:

g = 1; c = 123; true
g = 1; c = 456; false
g = 1; c = 789; false
g = 2; c = 123; false
g = 2; c = 456; true
g = 2; c = 789; false
g = 3; c = 123; false
g = 3; c = 456; false
g = 3; c = 789; false

Bạn không cần trích dẫn các biến [[như bạn làm [vì nó không phải là một lệnh riêng biệt theo cùng một cách [.


Đó không phải là một câu hỏi kinh điển sao?

Tôi đã có thể nghĩ như vậy. Tuy nhiên, có một cách khác, đó là:

if [ "$g" -eq 1 -a "$c" = "123" ] || [ "$g" -eq 2 -a "$c" = "456" ]
then echo abc
else echo efg
fi

Thật vậy, nếu bạn đọc hướng dẫn 'vỏ di động' cho autoconfcông cụ hoặc các gói liên quan, ký hiệu này - sử dụng ' ||' và ' &&' - là những gì họ khuyên dùng. Tôi cho rằng bạn thậm chí có thể đi xa đến mức:

if [ "$g" -eq 1 ] && [ "$c" = "123" ]
then echo abc
elif [ "$g" -eq 2 ] && [ "$c" = "456" ]
then echo abc
else echo efg
fi

Trường hợp các hành động tầm thường như tiếng vang, điều này không phải là xấu. Khi khối hành động được lặp lại là nhiều dòng, việc lặp lại quá đau đớn và một trong những phiên bản trước đó là thích hợp hơn - hoặc bạn cần bọc các hành động thành một chức năng được gọi trong các thenkhối khác nhau .


9
Thật tốt khi biết rằng dấu ngoặc đơn đã thoát; như một bên: trong trường hợp cụ thể này, các dấu ngoặc đơn thậm chí không cần thiết, bởi vì -athực sự có độ ưu tiên cao hơn -o(không giống &&||trong vỏ - tuy nhiên, bên trong các bash [[ ... ]] điều kiện , && cũng có độ ưu tiên cao hơn ||). Nếu bạn muốn tránh -a-ocho độ bền tối đa và tính di động - mà người đàn ông trang POSIX bản thân gợi ý - bạn cũng có thể sử dụng subshells cho nhóm:if ([ $g -eq 1 ] && [ "$c" = "123" ]) || ([ $g -eq 2 ] && [ "$c" = "456" ])
mklement0

Giải pháp tốt, nhưng tôi thích biểu mẫu mà không cần tất cả dấu ngoặc đơn và dấu ngoặc:if test "$g" -eq 1 -a "$c" = "123" || test "$g" -eq 2 -a "$c" = "456"; then ...
Mogens TrasherDK

Tôi hơi muộn với điều này, nhưng nó vẫn là kết quả tìm kiếm hàng đầu, vì vậy tôi chỉ muốn lưu ý rằng việc sử dụng &&hoặc ||cũng thích hợp hơn vì nó hoạt động giống như điều kiện trong các ngôn ngữ khác và cho phép bạn điều kiện ngắn mạch. Ví dụ : if [ -n "${check_inodes}" ] && [ "$(stat -f %i "/foo/bar")" = "$(stat -f %i "/foo/baz")" ]. Làm theo cách này, nếu check_inodestrống, bạn tránh hai cuộc gọi đến stat, trong khi điều kiện kiểm tra phức tạp, lớn hơn phải xử lý tất cả các đối số trước khi thực hiện (điều này cũng có thể dẫn đến lỗi nếu bạn quên hành vi đó).
Harastak

182

Trong Bash:

if [[ ( $g == 1 && $c == 123 ) || ( $g == 2 && $c == 456 ) ]]

9
Chắc chắn là cách tiếp cận tốt nhất trong bash. Như một bên: trong trường hợp cụ thể này, các dấu ngoặc đơn thậm chí không cần thiết, bởi vì bên trong các [[ ... ]] điều kiện && thực sự có độ ưu tiên cao hơn ||- không giống như bên ngoài các điều kiện như vậy.
mkuity0

Có cách nào chúng ta có thể tìm ra điều kiện phù hợp ở đây không? Đây có phải là một trong ngoặc đơn đầu tiên hoặc một trong những khác?
Firelord

1
@Firelord: Bạn cần tách các điều kiện thành một câu lệnh if/ elsevà đặt mã giữa thenfiđặt vào một hàm để tránh lặp lại nó. Trong các trường hợp rất đơn giản, bạn có thể sử dụng một casecâu lệnh với phần tiếp theo ;&(trong Bash 4).
Tạm dừng cho đến khi có thông báo mới.

1
Mục đích của hai dấu ngoặc vuông là gì?
peterchaula


37

Sử dụng /bin/bashnhư sau sẽ hoạt động:

if [ "$option" = "Y" ] || [ "$option" = "y" ]; then
    echo "Entered $option"
fi

8

Hãy cẩn thận nếu bạn có khoảng trắng trong các biến chuỗi của bạn và bạn kiểm tra sự tồn tại. Hãy chắc chắn để trích dẫn chúng đúng.

if [ ! "${somepath}" ] || [ ! "${otherstring}" ] || [ ! "${barstring}" ] ; then

1
khi chúng ta sử dụng dấu ngoặc nhọn sau ký hiệu đô la !!
YouAreAwgie

6
$ g=3
$ c=133
$ ([ "$g$c" = "1123" ] || [ "$g$c" = "2456" ]) && echo "abc" || echo "efg"
efg
$ g=1
$ c=123
$ ([ "$g$c" = "1123" ] || [ "$g$c" = "2456" ]) && echo "abc" || echo "efg"
abc

2
Đó là một subshell vô dụng. { ;}có thể được sử dụng thay thế.
phk

2

Trong bash để so sánh chuỗi, bạn có thể sử dụng kỹ thuật sau.

if [ $var OP "val" ]; then
    echo "statements"
fi

Thí dụ:

var="something"
if [ $var != "otherthing" ] && [ $var != "everything" ] && [ $var != "allthings" ]; then
    echo "this will be printed"
else
    echo "this will not be printed"
fi

0
#!/bin/bash

current_usage=$( df -h | grep 'gfsvg-gfslv' | awk {'print $5'} )
echo $current_usage
critical_usage=6%
warning_usage=3%

if [[ ${current_usage%?} -lt ${warning_usage%?} ]]; then
echo OK current usage is $current_usage
elif [[ ${current_usage%?} -ge ${warning_usage%?} ]] && [[ ${current_usage%?} -lt ${critical_usage%?} ]]; then
echo Warning $current_usage
else
echo Critical $current_usage
fi

3
Chào mừng bạn đến với Stack Overflow! Câu hỏi này đang tìm kiếm một lời giải thích , không chỉ đơn giản cho mã làm việc. Câu trả lời của bạn không cung cấp cái nhìn sâu sắc cho người hỏi và có thể bị xóa. Vui lòng chỉnh sửa để giải thích những gì gây ra các triệu chứng quan sát.
Toby Speight

0

bạn cũng có thể xâu chuỗi hơn 2 điều kiện:

if [ \( "$1" = '--usage' \) -o \( "$1" = '' \) -o \( "$1" = '--help' \) ]
then
   printf "\033[2J";printf "\033[0;0H"
   cat << EOF_PRINT_USAGE

   $0 - Purpose: upsert qto http json data to postgres db

   USAGE EXAMPLE:

   $0 -a foo -a bar



EOF_PRINT_USAGE
   exit 1
fi
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.