Verwenden des Operators "ungleich" für den Zeichenfolgenvergleich

117

Ich habe versucht zu überprüfen, ob die PHONE_TYPEVariable einen von drei gültigen Werten enthält.

if [ "$PHONE_TYPE" != "NORTEL" ] || [ "$PHONE_TYPE" != "NEC" ] ||
   [ "$PHONE_TYPE" != "CISCO" ]
then
    echo "Phone type must be nortel,cisco or nec"
    exit
fi

Der obige Code hat bei mir nicht funktioniert, also habe ich es stattdessen versucht:

if [ "$PHONE_TYPE" == "NORTEL" ] || [ "$PHONE_TYPE" == "NEC" ] ||
   [ "$PHONE_TYPE" == "CISCO" ]
then
    :        # do nothing
else
    echo "Phone type must be nortel,cisco or nec"
    exit
fi

Gibt es sauberere Wege für diese Art von Aufgabe?

münchen
quelle

Antworten:

162

Ich vermute, Sie suchen:

if [ "$PHONE_TYPE" != "NORTEL" ] && [ "$PHONE_TYPE" != "NEC" ] &&
   [ "$PHONE_TYPE" != "CISCO" ]

Die Regeln für diese Äquivalente werden als De-Morgan-Gesetze bezeichnet und bedeuten in Ihrem Fall:

not(A || B || C) => not(A) && not(B) && not (C)

Beachten Sie die Änderung des Booleschen Operators oder und und.

Während Sie versucht haben:

not(A || B || C) => not(A) || not(B) || not(C)

Was offensichtlich nicht funktioniert.

Nils Werner
quelle
28

Ein viel kürzerer Weg wäre:

if [[ ! $PHONE_TYPE =~ ^(NORTEL|NEC|CISCO)$ ]]; then 
  echo "Phone type must be nortel, cisco or nec."
fi
  • ^ - Zum Abgleichen eines Anfangs am Zeilenanfang
  • $ - Zum Zeilenende passen
  • =~ - Bashs eingebauter Vergleichsoperator für reguläre Ausdrücke
0x80
quelle
2
Ich denke, das sollte seinif [[ ! $PHONE_TYPE =~ ^(NORTEL|NEC|CISCO)$ ]]; then
Milan Simek
12

Gute Antworten und eine unschätzbare Lektion;) Ich möchte nur mit einer Notiz ergänzen.

Welche Art von Test man wählt, hängt stark von Code, Struktur, Umgebung usw. ab.

Eine Alternative könnte sein, einen Schalter oder eine caseAnweisung wie folgt zu verwenden:

case "$PHONE_TYPE" in
"NORTEL"|"NEC"|"CISCO")
    echo "OK"
    ;;
*)
    echo "Phone type must be nortel,cisco or nec"
    ;;
esac

Als zweite Anmerkung sollten Sie vorsichtig sein, wenn Sie Variablennamen in Großbuchstaben verwenden. Dies dient dazu, eine Kollision zwischen vom System eingegebenen Variablen zu verhindern, bei der es sich fast immer um Großbuchstaben handelt. Also $phone_typestatt $PHONE_TYPE.

Auch wenn dieser sicher ist, wenn Sie es sich zur Gewohnheit gemacht haben, Großbuchstaben zu verwenden, könnten IFS="boo"Sie eines Tages sagen, dass Sie sich in einer Welt voller Verletzungen befinden.

Es macht es auch einfacher zu erkennen, was was ist.

Nicht ein muss, sondern ein würde stark überlegen.


Es ist vermutlich auch ein guter Kandidat für eine Funktion. Dadurch ist der Code meist einfacher zu lesen und zu warten. Z.B:

valid_phone_type()
{
    case "$1" in
    "NORTEL"|"NEC")
        return 0;;
    *)
        echo "Model $1 is not supported"
        return 1;;
    esac
}

if ! valid_phone_type "$phone_type"; then
    echo "Bye."
    exit 1
fi
Runium
quelle
9

Sie sollten ANDs verwenden, nicht ORs.

if [ "$PHONE_TYPE" != "NORTEL" ] && [ "$PHONE_TYPE" != "NEC" ] && [ "$PHONE_TYPE" != "CISCO" ]
then

oder

if [ "$PHONE_TYPE" != "NORTEL" -a "$PHONE_TYPE" != "NEC" -a "$PHONE_TYPE" != "CISCO" ]
then
jlliagre
quelle
1

So korrigieren Sie eine der obigen Antworten (da ich noch keinen Kommentar abgeben kann):

PHONE_TYPE="NORTEL"
if [[ $PHONE_TYPE =~ ^(NORTEL|NEC|CISCO|SPACE TEL)$ ]]; then 
  echo "Phone type accepted."
else
  echo "Error! Phone type must be NORTEL, CISCO or NEC."
fi

Bitte beachten Sie, dass Sie mindestens Bash 4 für diese Verwendung von = ~ benötigen.
In Bash 3 funktioniert dies nicht.

Ich habe unter MS Windows 7 mit bash 4.3.46 (funktioniert einwandfrei) und bash 3.1.17 (hat nicht funktioniert) getestet

Die LHS von = ~ sollte in Anführungszeichen stehen. Oben würde auch PHONE_TYPE = "SPACE TEL" passen.

Wille
quelle
0

Verwenden Sie stattdessen [[

if [[ "$PHONE_TYPE" != "NORTEL" ]] || [[ "$PHONE_TYPE" != "NEC" ]] || 
   [[ "$PHONE_TYPE" != "CISCO" ]]
then
echo "Phone type must be nortel,cisco or nec"
exit 1
fi
Swapnil
quelle
2
Das ist natürlich falsch. [[vs [hilft nicht, wenn die Logik ausgeschaltet ist.
ilkkachu
0

Nur ein Variationsvorschlag basierend auf der @ 0x80-Lösung:

# define phone brand list
phoneBrandList=" NORTEL NEC CISCO" ## separator is space with an extra space in first place

# test if user given phone is contained in the list
if [[ ${phoneBrandList} =~ (^|[[:space:]])"${userPhoneBrand}"($|[[:space:]]) ]]; then
    echo "found it !"
fi
tdaget
quelle