Y a-t-il un opérateur «in» à bash / bourne?

17

Je recherche un opérateur «in» qui fonctionne comme ceci:

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

C'est évidemment une déclaration beaucoup plus courte par rapport à, disons, l'utilisation de plusieurs tests "ou".

mrjayviper
la source

Réponses:

31

Vous pouvez utiliser 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

Avec ksh, bash -O extglobou zsh -o kshglob, vous pouvez également utiliser un modèle glob étendu:

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

Avec bash, ksh93ou zsh, vous pouvez également utiliser une comparaison d'expressions régulières:

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
tournevis
la source
une raison pour les doubles crochets? Merci encore!
mrjayviper
1
@mrjayviper les parenthèses doubles sont une construction de test étendue - AFAIK l'opérateur regex =~n'est pas valide dans le test de parenthèse simple POSIX
steeldriver
1
@steeldriver Only [est POSIX, mais [[est une fonctionnalité étendue de bash, ksh (apparemment, il est originaire de là, et zsh. l' caseexemple est le plus POSIX de tous, cependant
Sergiy Kolodyazhnyy
2
@ StéphaneChazelas Depuis au moins bash 4.1-alpha, il n'est pas nécessaire de définir explicitement extglog. De changements bash : s. Forcer extglob temporairement lors de l'analyse de l'argument de modèle aux opérateurs == et! = À la commande [[, pour des raisons de compatibilité.
Isaac
@steeldriver Le $ 1 in case "$1" inn'a pas besoin d'être cité, aucun fractionnement de mot ni expansion de nom de chemin ne sont effectués dans ce jeton.
Isaac
9

Il n'y a pas de test "in" en bash, mais il y a un test regex (pas en bourne):

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

Et généralement écrit à l'aide d'une variable (moins de problèmes de citation):

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

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

Pour un shell Bourne plus ancien, vous devez utiliser une correspondance de casse:

case $1 in
    cat|dog|mouse)   echo "dollar 1 is either a cat or a dog or a mouse";;
esac
Isaac
la source
C'est la réponse acceptée pour moi.
Nam G VU du
6

L'utilisation d'un caseest très bien si vous avez un ensemble fixe d'animaux contre lesquels vous voulez faire correspondre. Mais cela ne fonctionnera pas si vous avez besoin de construire le modèle lors de l'exécution, car il casen'interprète pas l'alternance à partir des paramètres développés.

Cela correspondra uniquement à la chaîne littérale cat|dog|mouse:

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

Vous pouvez cependant utiliser une variable avec la correspondance d'expression régulière. Tant que la variable n'est pas citée, tous les opérateurs d'expression régulière qu'elle contient ont leur signification particulière.

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

Vous pouvez également utiliser des tableaux associatifs. Vérifier si une clé existe dans l'une est la chose la plus proche d'un inopérateur que Bash donne. Bien que la syntaxe soit un peu moche:

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

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

(se ${arr[$1]+x}développe en xsi arr[$1]est défini, vide sinon. )

ilkkachu
la source
5

Vous pourriez utiliser une caseinstruction dans un iftest, mais le code aurait l'air un peu poilu:

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 légèrement plus court,

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

Ceci utilise une caseclause juste pour faire la correspondance de modèle pour la ifclause. Il introduit un test vrai / faux inutile.

Il vaut mieux simplement utiliser 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

Ne faites pas ça:

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 ca:

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

... parce que vous ajoutez simplement une cruauté plus dangereuse, juste pour pouvoir utiliser une ifdéclaration dans votre code à la place d'une casedéclaration parfaitement raisonnable .

Kusalananda
la source
Ne serait-il pas préférable de diviser la casse en appel de fonction et d'évaluer l'état de sortie à l'intérieur de l'instruction if?
Sergiy Kolodyazhnyy
@SergiyKolodyazhnyy Et que le motif soit un argument de la fonction? Il faudrait faire une evalde la casedéclaration dans ce cas, et il serait encore plus sujettes à des erreurs.
Kusalananda
Dans ce cas, il y a un modèle simple, chacun pourrait être passé comme paramètre positionnel pour fonctionner et faire "$1"|"$2"|"$3". Aussi unix.stackexchange.com/a/234415/85039 Mais oui, c'est un peu poilu.
Sergiy Kolodyazhnyy
4

grep approche.

if echo $1 | grep -qE "^(cat|dog|mouse)$"; then 
    echo "dollar 1 is either a cat or a dog or a mouse"
fi
  • -qpour éviter toute sortie à l'écran (plus rapide à taper que >/dev/null).
  • -Epour les expressions régulières étendues, les (cat|dog|mouse)aspects en ont besoin.
  • ^(cat|dog|mouse)$correspond à toutes les lignes commençant par ( ^) par chat, chien ou souris ( (cat|dog|mouse)) suivi de fin de ligne ( $)
Steve
la source