Có một nhà điều hành trong vùng bash / bourne không?


17

Tôi đang tìm kiếm một nhà điều hành trên mạng có chức năng như thế này:

if [ "$1" in ("cat","dog","mouse") ]; then
    echo "dollar 1 is either a cat or a dog or a mouse"
fi

Đó rõ ràng là một tuyên bố ngắn hơn nhiều so với, sử dụng một số bài kiểm tra "hoặc".

Câu trả lời:


31

Bạn có thể sử dụng case...esac

$ cat in.sh 
#!/bin/bash

case "$1" in 
  "cat"|"dog"|"mouse")
    echo "dollar 1 is either a cat or a dog or a mouse"
  ;;
  *)
    echo "none of the above"
  ;;
esac

Ví dụ.

$ ./in.sh dog
dollar 1 is either a cat or a dog or a mouse
$ ./in.sh hamster
none of the above

Với ksh, bash -O extglobhoặc zsh -o kshglob, bạn cũng có thể sử dụng mô hình toàn cầu mở rộng:

if [[ "$1" = @(cat|dog|mouse) ]]; then
  echo "dollar 1 is either a cat or a dog or a mouse"
else
  echo "none of the above"
fi

Với bash, ksh93hoặc zsh, bạn cũng có thể sử dụng so sánh biểu thức chính quy:

if [[ "$1" =~ ^(cat|dog|mouse)$ ]]; then
  echo "dollar 1 is either a cat or a dog or a mouse"
else
  echo "none of the above"
fi

bất kỳ lý do cho dấu ngoặc kép? Cảm ơn một lần nữa!
mrjayviper

1
@mrjayviper dấu ngoặc kép là cấu trúc thử nghiệm mở rộng - AFAIK toán tử regex =~không hợp lệ trong thử nghiệm khung đơn POSIX
Steeldo

1
@steel ấn Chỉ [là POSIX, nhưng [[là tính năng mở rộng của bash, ksh (hình như nó bắt nguồn từ đó, và caseví dụ zsh là hầu hết POSIX của tất cả
Sergiy Kolodyazhnyy

2
@ StéphaneChazelas Vì ít nhất bash 4.1-alpha không cần thiết lập extglog một cách rõ ràng. Từ bash thay đổi : s. Buộc extglob tạm thời khi phân tích đối số mẫu cho các toán tử == và! = Cho lệnh [[, để tương thích.
Isaac

@steel ấn $ 1 in case "$1" inkhông cần trích dẫn, không có phân tách từ cũng như mở rộng tên đường dẫn được thực hiện trong mã thông báo đó.
Isaac

9

Không có kiểm tra "trong" trong bash, nhưng có một bài kiểm tra regex (không phải trong bourne):

if [[ $1 =~ ^(cat|dog|mouse)$ ]]; then
    echo "dollar 1 is either a cat or a dog or a mouse"
fi

Và thường được viết bằng một biến (ít vấn đề hơn với trích dẫn):

regex='^(cat|dog|mouse)$'

if [[ $1 =~ $regex ]]; then
    echo "dollar 1 is either a cat or a dog or a mouse"
fi

Đối với vỏ Bourne cũ hơn, bạn cần sử dụng khớp trường hợp:

case $1 in
    cat|dog|mouse)   echo "dollar 1 is either a cat or a dog or a mouse";;
esac

Đây là câu trả lời được chấp nhận cho tôi.
Nam G VU

6

Sử dụng một caselà tốt và tốt nếu bạn có một bộ vật nuôi cố định mà bạn muốn phù hợp với. Nhưng nó sẽ không hoạt động nếu bạn cần xây dựng mô hình trong thời gian chạy, vì casekhông diễn giải sự thay thế từ bên trong các tham số mở rộng.

Điều này sẽ chỉ phù hợp với chuỗi chữ cat|dog|mouse:

patt='cat|dog|mouse'
case $1 in 
        $patt) echo "$1 matches the case" ;; 
esac

Tuy nhiên, bạn có thể sử dụng một biến có khớp biểu thức chính quy. Miễn là biến không được trích dẫn, bất kỳ toán tử regex nào trong nó đều có ý nghĩa đặc biệt của chúng.

patt='cat|dog|mouse'
if [[ "$1" =~ ^($patt)$ ]]; then
        echo "$1 matches the pattern"
fi

Bạn cũng có thể sử dụng mảng kết hợp. Kiểm tra xem một khóa tồn tại trong một là điều gần nhất với intoán tử mà Bash đưa ra. Mặc dù cú pháp hơi xấu:

declare -A arr
arr[cat]=1
arr[dog]=1
arr[mouse]=1

if [ "${arr[$1]+x}" ]; then
        echo "$1 is in the array"
fi

( ${arr[$1]+x}mở rộng thành xnếu arr[$1]được đặt, trống nếu không. )


5

Bạn có thể sử dụng một casecâu lệnh trong một ifbài kiểm tra, nhưng mã sẽ trông hơi nhiều lông:

if case "$1" in (cat|dog|mouse) true ;; (*) false; esac; then
    printf '"%s" is one of cat, dog or mouse\n' "$1"
else
    printf '"%s" is unknown\n' "$1"
fi

hoặc ngắn hơn một chút,

if ! case "$1" in (cat|dog|mouse) false; esac; then
    printf '"%s" is one of cat, dog or mouse\n' "$1"
else
    printf '"%s" is unknown\n' "$1"
fi

Điều này đang sử dụng một casemệnh đề chỉ để thực hiện khớp mẫu cho ifmệnh đề. Nó giới thiệu một bài kiểm tra đúng / sai không cần thiết.

Tốt hơn hết là chỉ sử dụng case:

case "$1" in
    cat|dog|mouse)
        printf '"%s" is one of cat, dog or mouse\n' "$1"
        ;;
    *)
        printf '"%s" is unknown\n' "$1"
esac

Đừng làm điều này:

is_one_of () {
    eval "case $1 in ($2) return 0; esac"
    return 1
}

if is_one_of "$1" 'cat|dog|mouse'; then
    printf '"%s" is one of cat, dog or mouse\n' "$1"
else
    printf '"%s" is unknown\n' "$1"
fi

hoặc này:

is_one_of () (
    word=$1
    shift
    IFS='|'
    eval "case $word in ($*) return 0; esac"
    return 1
)

if is_one_of "$1" cat dog mouse; then
    printf '"%s" is one of cat, dog or mouse\n' "$1"
else
    printf '"%s" is unknown\n' "$1"
fi

... bởi vì bạn chỉ cần thêm hành trình nguy hiểm hơn, để có thể sử dụng một ifcâu lệnh trong mã của bạn thay cho một casecâu lệnh hoàn toàn hợp lý .


Sẽ không tốt hơn nếu phân tách trường hợp thành lệnh gọi hàm và đánh giá trạng thái thoát bên trong câu lệnh if?
Sergiy Kolodyazhnyy

@SergiyKolodyazhnyy Và hãy để mô hình là một đối số cho hàm? Nó sẽ phải làm một evaltrong những casetuyên bố trong trường hợp đó, và nó sẽ càng dễ bị lỗi.
Kusalananda

Trong trường hợp này có mẫu đơn giản, mỗi mẫu có thể được truyền dưới dạng tham số vị trí cho hàm và thực hiện "$1"|"$2"|"$3". Ngoài ra unix.stackexchange.com/a/234415/85039 Nhưng vâng, nó hơi nhiều lông.
Sergiy Kolodyazhnyy

4

grep tiếp cận.

if echo $1 | grep -qE "^(cat|dog|mouse)$"; then 
    echo "dollar 1 is either a cat or a dog or a mouse"
fi
  • -qđể tránh mọi đầu ra ra màn hình (gõ nhanh hơn >/dev/null).
  • -Echo các (cat|dog|mouse)khía cạnh biểu thức chính quy mở rộng cần điều này.
  • ^(cat|dog|mouse)$khớp với bất kỳ dòng nào bắt đầu ( ^) với mèo, chó hoặc chuột ( (cat|dog|mouse)) theo sau là cuối dò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.