Gibt es einen In-Operator in bash / bourne?

17

Ich suche einen "in" -Operator, der ungefähr so ​​funktioniert:

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

Dies ist offensichtlich eine viel kürzere Aussage als beispielsweise die Verwendung mehrerer "oder" Tests.

mrjayviper
quelle

Antworten:

31

Sie können 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

Mit ksh, bash -O extgloboder zsh -o kshglobkönnten Sie auch einen erweiterten glob Muster verwenden:

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

Mit bash, ksh93oder zshkönnten Sie auch einen regulären Ausdruck Vergleich verwenden:

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
Stahlfahrer
quelle
Gibt es einen Grund für die doppelten Klammern? Danke noch einmal!
Mrjayviper
1
@mrjayviper die doppelten Klammern sind ein erweitertes Testkonstrukt - AFAIK, der Regex-Operator, =~ist im POSIX-Einzelklammertest nicht gültig
steeldriver
1
@steeldriver Only [ist POSIX, ist aber [[ein erweitertes Feature von bash, ksh (anscheinend stammt es von dort, und zsh. caseBeispiel ist das meiste POSIX von allen
Sergiy Kolodyazhnyy
2
@ StéphaneChazelas Seit mindestens Bash 4.1-Alpha muss Extglog nicht mehr explizit gesetzt werden. Von Bash-Änderungen : s. Erzwingen Sie das temporäre Aktivieren von extglob, wenn Sie das Musterargument für die Operatoren == und! = Mit dem Befehl [[aus Kompatibilitätsgründen analysieren.
Isaac
@steeldriver Das $ 1 in case "$1" inmuss nicht in Anführungszeichen gesetzt werden. In diesem Token wird weder eine Wortteilung noch eine Pfadnamenerweiterung durchgeführt.
Isaac
9

Es gibt keinen "in" -Test in bash, aber einen Regex-Test (nicht in bourne):

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

Und normalerweise mit einer Variablen geschrieben (weniger Probleme mit Zitieren):

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

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

Für eine ältere Bourne-Shell müssen Sie eine Groß- / Kleinschreibung verwenden:

case $1 in
    cat|dog|mouse)   echo "dollar 1 is either a cat or a dog or a mouse";;
esac
Isaac
quelle
Dies ist die akzeptierte Antwort für mich.
Nam G VU
6

Die Verwendung von a caseist in Ordnung und gut, wenn Sie eine feste Anzahl von Haustieren haben, mit denen Sie übereinstimmen möchten. Es wird jedoch nicht funktionieren, wenn Sie das Muster zur Laufzeit erstellen müssen, da casedie Abwechslung nicht in erweiterten Parametern interpretiert wird.

Dies wird nur mit der Literalzeichenfolge übereinstimmen cat|dog|mouse:

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

Sie können jedoch eine Variable mit dem regulären Ausdruck match verwenden. Solange die Variable nicht in Anführungszeichen gesetzt ist, haben alle Regex-Operatoren in ihr ihre spezielle Bedeutung.

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

Sie können auch assoziative Arrays verwenden. Die Prüfung, ob ein Schlüssel in einem Schlüssel vorhanden ist, kommt dem von inBash angegebenen Operator am nächsten . Die Syntax ist allerdings etwas hässlich:

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}Erweitert um xwenn arr[$1]gesetzt, sonst leer. )

ilkkachu
quelle
5

Sie könnten eine caseAnweisung in einem ifTest verwenden, aber der Code würde ein bisschen haarig aussehen:

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

oder etwas kürzer,

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

Hierbei wird eine caseKlausel verwendet, um nur den Mustervergleich für die ifKlausel durchzuführen. Es wird ein unnötiger Wahr / Falsch-Test eingeführt.

Es ist besser, nur zu verwenden 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

Mach das nicht:

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

oder dieses:

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

... weil Sie nur eine gefährlichere Cruft hinzufügen, nur um eine ifAnweisung in Ihrem Code anstelle einer absolut vernünftigen caseAnweisung verwenden zu können.

Kusalananda
quelle
Wäre es nicht besser, die Groß- und Kleinschreibung in Funktionsaufrufe aufzuteilen und den Exit-Status in der if-Anweisung auszuwerten?
Sergiy Kolodyazhnyy
@SergiyKolodyazhnyy Und lassen Sie das Muster ein Argument für die Funktion sein? In diesem Fall müsste es eine evalder caseAnweisungen ausführen, und es wäre noch fehleranfälliger.
Kusalananda
In diesem Fall gibt es ein einfaches Muster, von dem jedes als Positionsparameter übergeben werden kann, um zu funktionieren und zu tun "$1"|"$2"|"$3". Auch unix.stackexchange.com/a/234415/85039 Aber ja, es ist ein bisschen haarig.
Sergiy Kolodyazhnyy
4

grep Ansatz.

if echo $1 | grep -qE "^(cat|dog|mouse)$"; then 
    echo "dollar 1 is either a cat or a dog or a mouse"
fi
  • -qum jegliche Ausgabe auf dem Bildschirm zu vermeiden (schneller zu tippen als >/dev/null).
  • -Efür erweiterte reguläre Ausdrücke (cat|dog|mouse)benötigt aspekte dies.
  • ^(cat|dog|mouse)$Stimmt mit Zeilen überein, die ^mit Katze, Hund oder Maus beginnen ( (cat|dog|mouse)), gefolgt von Zeilenende ( $)
Steve
quelle