Czy w bash / bourne jest operator „in”?


17

Szukam operatora „w”, który działa mniej więcej tak:

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

Jest to oczywiście znacznie krótsze stwierdzenie w porównaniu do, powiedzmy, użycia kilku „lub” testów.

Odpowiedzi:


31

Możesz użyć 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

Dawny.

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

Z ksh, bash -O extgloblub zsh -o kshglob, można też użyć rozszerzoną glob wzoru:

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

Z bash, ksh93lub zsh, można też użyć wyrażenia regularnego porównania:

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

jakiś powód dla podwójnych nawiasów? Dzięki jeszcze raz!
mrjayviper

1
@ mrjayviper podwójne nawiasy kwadratowe są rozszerzoną konstrukcją testową - AFAIK operator wyrażenia regularnego =~nie jest prawidłowy w teście POSIX pojedynczego nawiasu
steeldriver

1
@steeldriver Only [jest POSIX, ale [[jest rozszerzoną funkcją bash, ksh (najwyraźniej pochodzi z tego miejsca, a zsh. caseprzykład jest najbardziej POSIX ze wszystkich
Sergiy Kolodyazhnyy

2
@ StéphaneChazelas Ponieważ przynajmniej bash 4.1-alfa nie ma potrzeby jawnego ustawiania extglog. Od zmian bash : s. Wymuś tymczasowe włączenie extglob podczas analizowania argumentu wzorca do operatorów == i! = Do polecenia [[w celu zapewnienia zgodności.
Izaak

@steeldriver 1 $ w case "$1" innie musi być cytowany, żadne dzielenie słów ani interpretacja nazw ścieżek nie są wykonywane w tym tokenie.
Isaac

9

Nie ma testu „w” w bash, ale istnieje test wyrażenia regularnego (nie w bourne):

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

I zwykle napisane przy użyciu zmiennej (mniej problemów z cytowaniem):

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

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

W przypadku starszej powłoki Bourne'a należy użyć dopasowania wielkości liter:

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

To jest dla mnie zaakceptowana odpowiedź.
Nam G VU

6

Używanie a casejest w porządku i dobrze, jeśli masz ustalony zestaw zwierząt, z którymi chcesz się równać. Ale to nie zadziała, jeśli trzeba zbudować wzorzec w środowisku wykonawczym, ponieważ casenie interpretuje on naprzemienności z rozszerzonych parametrów.

Spowoduje to dopasowanie tylko literału cat|dog|mouse:

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

Możesz jednak użyć zmiennej z dopasowaniem wyrażenia regularnego. Dopóki zmienna nie jest cytowana, wszystkie operatory wyrażenia regularnego w niej zawarte mają swoje specjalne znaczenie.

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

Możesz także użyć tablic asocjacyjnych. Sprawdzenie, czy klucz istnieje w jednym, jest najbliższe inoperatorowi, który daje Bash. Chociaż składnia jest nieco brzydka:

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}rozwija się do xif arr[$1]jest ustawione, w przeciwnym razie puste ).


5

Ty mógł używać caseoświadczenie w ifteście, ale kod będzie wyglądać nieco niebezpieczny:

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

lub nieco krótszy,

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

To jest użycie caseklauzuli tylko w celu dopasowania wzorca dla ifklauzuli. Wprowadza niepotrzebny test prawda / fałsz.

Lepiej po prostu użyć 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

Nie rób tego:

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

albo to:

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

... ponieważ dodajesz bardziej niebezpieczny cruft, aby móc użyć ifinstrukcji w kodzie zamiast całkowicie rozsądnej caseinstrukcji.


Czy nie byłoby lepiej podzielić wielkość liter na wywołanie funkcji i ocenić status wyjścia w instrukcji if?
Sergiy Kolodyazhnyy

@SergiyKolodyazhnyy I niech wzór będzie argumentem funkcji? W takim przypadku musiałby wykonać jedno evalze casestwierdzeń i byłby jeszcze bardziej podatny na błędy.
Kusalananda

W tym przypadku istnieje prosty wzorzec, każdy można przekazać jako parametr pozycyjny do działania i wykonania "$1"|"$2"|"$3". Również unix.stackexchange.com/a/234415/85039 Ale tak, to trochę owłosione.
Sergiy Kolodyazhnyy

4

grep podejście.

if echo $1 | grep -qE "^(cat|dog|mouse)$"; then 
    echo "dollar 1 is either a cat or a dog or a mouse"
fi
  • -qaby uniknąć wyświetlania danych wyjściowych (szybsze pisanie niż >/dev/null).
  • -E(cat|dog|mouse)wymagają tego aspekty rozszerzonych wyrażeń regularnych .
  • ^(cat|dog|mouse)$dopasowuje dowolne linie zaczynające się ( ^) od kota, psa lub myszy ( (cat|dog|mouse)), a następnie koniec linii ( $)
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.