Чи є оператор "in" в bash / bourne?


17

Я шукаю оператора "в", який працює приблизно так:

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

Очевидно, це набагато коротше твердження порівняно з, скажімо, використанням декількох тестів "чи".

Відповіді:


31

Ви можете використовувати 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

Вих.

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

За допомогою ksh, bash -O extglobабо zsh -o kshglob, ви також можете використовувати розширений шаблон глобуса:

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

З bash, ksh93або zsh, ви також можете використовувати звичайне порівняння виразів:

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

якась причина для подвійних дужок? Знову дякую!
mrjayviper

1
@mrjayviper подвійні дужки - це розширена тестова конструкція - AFAIK оператор регулярних =~
виразів

1
@steeldriver Тільки [POSIX, але [[це розширена функція bash, ksh (мабуть, це походить звідти, і zsh. caseприклад є найбільш POSIX з усіх, хоча
Сергій Колодяжний

2
@ StéphaneChazelas Оскільки принаймні bash 4.1-альфа, не потрібно чітко встановлювати extglog. Від баш змін : s. Примушуйте exglob увімкнено тимчасово, коли аналізує аргумент шаблону на оператори == і! = До команди [[для сумісності.
Ісаак

@steeldriver $ 1 у case "$1" inне потрібно цитувати, ні розділення слів, ні розширення імені не виконуються в цьому маркері.
Ісаак

9

Немає "in" тесту в bash, але є тест на регулярний вираз (не в bourne):

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

І зазвичай пишеться за допомогою змінної (менше проблем із цитуванням):

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

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

Для старшої оболонки Борна потрібно використовувати збіг справ:

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

Це прийнята для мене відповідь.
Нам Г ВУ

6

Використання caseштрафу - це добре і добре, якщо у вас є фіксований набір домашніх тварин, з якими ви хочете відповідати. Але це не спрацює, якщо вам потрібно побудувати візерунок під час виконання, оскільки caseвін не інтерпретує чергування з розширених параметрів.

Це відповідатиме лише буквальному рядку cat|dog|mouse:

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

Однак ви можете використовувати змінну з відповідним регулярним виразом. Поки змінна не цитується, будь-які оператори регулярних виразів в ній мають своє особливе значення.

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

Ви також можете використовувати асоціативні масиви. Перевірка наявності ключа в одному є найближчим до inоператора, який дає Bash. Хоча синтаксис трохи некрасивий:

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}розгортається до параметра, xякщо arr[$1]встановлено, інакше порожнє. )


5

Ви можете використовувати caseоператор у ifтесті, але код виглядає трохи волохатим:

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

або трохи коротше,

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

Для цього використовується caseпункт лише для того, щоб виконати відповідність шаблону для цього ifпункту. Це вводить зайвий істинний / хибний тест.

Краще просто використовувати 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

Не робіть цього:

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

або це:

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

... тому що ви просто додаєте більш небезпечну криптовалюту, просто щоб мати можливість використовувати ifоператор у своєму коді замість цілком розумного caseтвердження.


Хіба не було б краще розділити регістр на виклик функції та оцінити стан виходу всередині оператора if?
Сергій Колодяжний

@SergiyKolodyazhnyy І нехай шаблон буде аргументом функції? Було б повинно робити evalз caseзаяви в цьому випадку, і це було б ще більш схильні до помилок.
Кусалаланда

У цьому випадку є простий шаблон, кожен може передаватися як позиційний параметр, щоб функціонувати і робити "$1"|"$2"|"$3". Також unix.stackexchange.com/a/234415/85039 Але так, це трохи волохато.
Сергій Колодяжний

4

grep підхід.

if echo $1 | grep -qE "^(cat|dog|mouse)$"; then 
    echo "dollar 1 is either a cat or a dog or a mouse"
fi
  • -qщоб уникнути виводу на екран (швидше вводити, ніж >/dev/null).
  • -Eдля розширених (cat|dog|mouse)аспектів регулярних виразів цього потрібно.
  • ^(cat|dog|mouse)$відповідає будь-яким рядкам, що починається ( ^) з кішкою, собакою чи мишкою ( (cat|dog|mouse)), а потім кінцем рядка ( $)
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.