Existe um operador “in” no bash / bourne?

15

Estou procurando um operador "in" que funcione assim:

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

É obviamente uma declaração muito mais curta comparada, digamos, com vários "ou" testes.

    
por mrjayviper 02.06.2018 / 03:56

5 respostas

29

Você pode usar 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

Ex.

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

Com ksh , bash -O extglob ou zsh -o kshglob , você também pode usar um padrão glob ampliado:

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

Com bash , ksh93 ou zsh , você também pode usar uma comparação de expressões regulares:

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
    
por 02.06.2018 / 04:04
8

Não há um teste "in" no bash, mas há um teste de regex (não no bourne):

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

E geralmente escrito usando uma variável (menos problemas com citações):

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

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

Para um shell Bourne mais antigo, é necessário usar uma correspondência de maiúsculas e minúsculas:

case $1 in
    cat|dog|mouse)   echo "dollar 1 is either a cat or a dog or a mouse";;
esac
    
por 02.06.2018 / 12:05
6

Usar um case é bom e bom se você tiver um conjunto fixo de animais de estimação com o qual deseja combinar. Mas não funcionará se você precisar construir o padrão em tempo de execução, pois case não interpreta a alternação de dentro dos parâmetros expandidos.

Isso corresponderá somente à string literal cat|dog|mouse :

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

Você pode, no entanto, usar uma variável com a correspondência de expressão regular. Contanto que a variável não seja citada, qualquer operador regex dentro dela terá seus significados especiais.

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

Você também pode usar matrizes associativas. Verificar se uma chave existe em uma é a coisa mais próxima de um operador in que o Bash fornece. Embora a sintaxe seja um pouco feia:

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} expande para x se arr[$1] estiver definido, caso contrário vazio. )

    
por 02.06.2018 / 09:53
5

Você poderia usar uma instrução case em um teste if , mas o código pareceria um pouco complicado:

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

ou um pouco mais curto,

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

Isso está usando uma cláusula case apenas para fazer a correspondência de padrões para a cláusula if . Introduz um teste verdadeiro / falso desnecessário.

É melhor usar apenas 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

Não faça isso:

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

ou isto:

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

... porque você está apenas adicionando código mais perigoso, apenas para poder usar uma instrução if em seu código no lugar de uma instrução case perfeitamente razoável.

    
por 02.06.2018 / 08:52
4

grep abordagem.

if echo $1 | grep -qE "^(cat|dog|mouse)$"; then 
    echo "dollar 1 is either a cat or a dog or a mouse"
fi
  • -q para evitar qualquer saída para a tela (mais rápido para digitar do que >/dev/null ).
  • -E para expressões regulares estendidas (cat|dog|mouse) aspects precisa disso.
  • ^(cat|dog|mouse)$ corresponde a qualquer linha iniciada ( ^ ) com gato, cão ou mouse ( (cat|dog|mouse) ) seguido pelo fim da linha ( $ )
por 02.06.2018 / 11:06