Esiste un operatore "in" in bash / bourne?


17

Sto cercando un operatore "in" che funzioni in questo modo:

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

È ovviamente una dichiarazione molto più breve rispetto, per esempio, all'utilizzo di diversi test "o".

Risposte:


31

Puoi usare 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

Con ksh, bash -O extglobo zsh -o kshglob, puoi anche usare un modello glob esteso:

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

Con bash, ksh93o zsh, puoi anche usare un confronto di espressioni regolari:

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

qualche motivo per le doppie parentesi? Grazie ancora!
mrjayviper,

1
@mrjayviper le doppie parentesi sono un costrutto di test esteso - AFAIK l'operatore regex =~non è valido nel test POSIX a parentesi singola
steeldriver

1
@steeldriver [è solo POSIX, ma [[è una funzionalità estesa di bash, ksh (a quanto pare è originata da lì, e zsh. caseexample è la maggior parte di POSIX di tutti, però
Sergiy Kolodyazhnyy

2
@ StéphaneChazelas Dal momento che almeno bash 4.1-alpha non è necessario impostare esplicitamente extglog. Dalle modifiche bash : s. Forza extglob temporaneamente quando analizza l'argomento pattern agli operatori == e! = Al comando [[, per compatibilità.
Isaac,

@steeldriver Il $ 1 in case "$1" innon ha bisogno di essere quotato, nessuna suddivisione di parole né espansione del percorso vengono eseguite in quel token.
Isaac,

9

Non esiste un test "in" in bash, ma esiste un test regex (non in bourne):

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

E di solito scritto usando una variabile (meno problemi con il preventivo):

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

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

Per una vecchia shell Bourne è necessario utilizzare una corrispondenza maiuscola:

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

Questa è la risposta accettata per me.
Nam G VU

6

Usare a caseva bene e bene se hai un set fisso di animali domestici con cui vuoi confrontarti. Ma non funzionerà se è necessario creare il modello in fase di esecuzione, poiché casenon interpreta l'alternanza all'interno dei parametri espansi.

Questo corrisponderà solo alla stringa letterale cat|dog|mouse:

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

Tuttavia, puoi utilizzare una variabile con la corrispondenza dell'espressione regolare. Finché la variabile non viene quotata, tutti gli operatori regex al suo interno hanno i loro significati speciali.

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

È inoltre possibile utilizzare array associativi. Controllare se esiste una chiave in una è la cosa più vicina a un inoperatore che Bash fornisce. Sebbene la sintassi sia un po 'brutta:

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

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

(si ${arr[$1]+x}espande in xif arr[$1]è impostato, svuota altrimenti. )


5

È possibile utilizzare caseun'istruzione in un iftest, ma il codice sembrerebbe un po 'peloso:

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

o leggermente più corto,

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

Questo sta usando una caseclausola solo per fare la corrispondenza del modello per la ifclausola. Introduce un test vero / falso non necessario.

È meglio usare solo 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

Non farlo:

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

o questo:

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

... perché stai solo aggiungendo una cruft più pericolosa, solo per essere in grado di utilizzare ifun'istruzione nel tuo codice al posto di caseun'istruzione perfettamente ragionevole .


Non sarebbe meglio dividere il caso in chiamata di funzione e valutare lo stato di uscita all'interno dell'istruzione if?
Sergiy Kolodyazhnyy il

@SergiyKolodyazhnyy E lasciare che lo schema sia un argomento per la funzione? Dovrebbe fare una evaldelle casedichiarazioni in quel caso, e sarebbe ancora più incline agli errori.
Kusalananda

In questo caso c'è un modello semplice, ognuno potrebbe essere passato come parametro posizionale per funzionare e fare "$1"|"$2"|"$3". Anche unix.stackexchange.com/a/234415/85039 Ma sì, è un po 'peloso.
Sergiy Kolodyazhnyy il

4

grep approccio.

if echo $1 | grep -qE "^(cat|dog|mouse)$"; then 
    echo "dollar 1 is either a cat or a dog or a mouse"
fi
  • -qper evitare qualsiasi output sullo schermo (più veloce da digitare di >/dev/null).
  • -Eper estese espressioni regolari gli (cat|dog|mouse)aspetti ne hanno bisogno.
  • ^(cat|dog|mouse)$corrisponde a tutte le righe che iniziano ( ^) con cat, dog o mouse ( (cat|dog|mouse)) seguite dalla fine della linea ( $)
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.