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ń?

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.


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

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

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

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.


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

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
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.