Używanie nierównomiernego operatora do porównywania ciągów

117

Próbowałem sprawdzić, czy PHONE_TYPEzmienna zawiera jedną z trzech prawidłowych wartości.

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

Powyższy kod nie działał dla mnie, więc zamiast tego spróbowałem:

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

Czy istnieją czystsze sposoby wykonywania tego typu zadań?

Munish
źródło

Odpowiedzi:

162

Chyba szukasz:

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

Reguły dla tych ekwiwalentów są nazywane prawami De Morgana, a w twoim przypadku oznaczają:

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

Zwróć uwagę na zmianę operatora logicznego lub i i.

Podczas gdy próbowałeś zrobić:

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

Co oczywiście nie działa.

Nils Werner
źródło
28

Znacznie krótszą drogą byłoby:

if [[ ! $PHONE_TYPE =~ ^(NORTEL|NEC|CISCO)$ ]]; then 
  echo "Phone type must be nortel, cisco or nec."
fi
  • ^ - Aby dopasować początek na początku linii
  • $ - Aby dopasować koniec linii
  • =~ - Wbudowany operator porównania wyrażeń regularnych Bash
0x80
źródło
2
Myślę, że tak powinno byćif [[ ! $PHONE_TYPE =~ ^(NORTEL|NEC|CISCO)$ ]]; then
Milan Simek
12

Dobre odpowiedzi i bezcenna lekcja;) Chcę tylko uzupełnić notatką.

Wybór testu zależy w dużym stopniu od kodu, struktury, otoczenia itp.

Alternatywą może być użycie przełącznika lub caseinstrukcji jak w:

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

Jako drugą uwagę należy zachować ostrożność, używając nazw zmiennych pisanych dużymi literami. Ma to na celu uniknięcie kolizji między zmiennymi wprowadzanymi przez system, które prawie zawsze są wielkimi literami. Dlatego $phone_typezamiast $PHONE_TYPE.

Chociaż ten jest bezpieczny, jeśli masz nawyk używania wielkich liter, pewnego dnia możesz powiedzieć IFS="boo"i jesteś w świecie bólu.

Ułatwi również dostrzeżenie, co jest tym.

Nie trzeba, ale zdecydowanie powinien rozważyć.


Przypuszczalnie jest to również dobry kandydat na funkcję. To głównie sprawia, że ​​kod jest łatwiejszy do odczytania i utrzymania. Na przykład:

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
źródło
9

Powinieneś używać AND, a nie OR.

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

lub

if [ "$PHONE_TYPE" != "NORTEL" -a "$PHONE_TYPE" != "NEC" -a "$PHONE_TYPE" != "CISCO" ]
then
jlliagre
źródło
1

Aby poprawić powyższą odpowiedź (ponieważ nie mogę jeszcze komentować):

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

Pamiętaj, że potrzebujesz przynajmniej bash 4 do tego użycia = ~
Nie działa w bash 3.

Testowałem na MS Windows 7 używając bash 4.3.46 (działa dobrze) i bash 3.1.17 (nie działał)

LHS = = powinno być w cudzysłowach. Powyżej PHONE_TYPE = „SPACE TEL” również pasowałby.

Będzie
źródło
0

Użyj [[zamiast

if [[ "$PHONE_TYPE" != "NORTEL" ]] || [[ "$PHONE_TYPE" != "NEC" ]] || 
   [[ "$PHONE_TYPE" != "CISCO" ]]
then
echo "Phone type must be nortel,cisco or nec"
exit 1
fi
Swapnil
źródło
2
To oczywiście źle. [[vs [nie pomaga przy wyłączonej logice.
ilkkachu
0

Tylko propozycja zmiany oparta na rozwiązaniu @ 0x80:

# 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
źródło