Використання нерівного оператора для порівняння рядків


117

Я спробував перевірити, чи PHONE_TYPEзмінна містить одне з трьох дійсних значень.

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

Вищеописаний код не працював для мене, тому я спробував це:

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

Чи є більш чіткі способи для такого типу завдань?

Відповіді:


162

Я думаю, ви шукаєте:

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

Правила цих еквівалентів називаються законами Де Моргана і у вашому випадку означають:

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

Зверніть увагу на зміну булевого оператора або і і та.

Беручи до уваги:

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

Що очевидно не працює.


28

Набагато коротший спосіб був би:

if [[ ! $PHONE_TYPE =~ ^(NORTEL|NEC|CISCO)$ ]]; then 
  echo "Phone type must be nortel, cisco or nec."
fi
  • ^ - Для відповідності старту на початку рядка
  • $ - Зрівняти кінець рядка
  • =~ - Вбудований оператор порівняння регулярних виразів Bash

2
Я думаю, що так має бутиif [[ ! $PHONE_TYPE =~ ^(NORTEL|NEC|CISCO)$ ]]; then
Мілан Симек

12

Гарні відповіді та безцінний урок;) Хочеться лише доповнити конспектом.

Який тип тесту вибрати, дуже залежить від коду, структури, оточення тощо.

Альтернативою може бути використання перемикача або caseзаяви, як у:

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

В якості другої примітки слід бути обережними, використовуючи великі імена змінних. Це потрібно для запобігання зіткнення змінних, введених системою, що майже завжди є всіма великими літерами. Таким чином $phone_typeзамість $PHONE_TYPE.

Хоча це і є безпечним, якщо ви звикли використовувати всі верхні регістри, одного дня ви можете сказати, IFS="boo"і ви перебуваєте в світі боляче.

Це також спростить помітити, що є чим.

Не обов'язково, але дуже б врахував.


Це також, мабуть, хороший кандидат на функцію. Це здебільшого полегшує читання та підтримку коду. Наприклад:

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

Ви повинні використовувати AND, а не АБО.

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

або

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

1

Щоб виправити вищезазначену відповідь (оскільки я поки не можу коментувати):

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

Зверніть увагу, що вам потрібно принаймні bash 4 для цього використання = ~
Це не працює в bash 3.

Я тестував на MS Windows 7, використовуючи bash 4.3.46 (працює нормально) та bash 3.1.17 (не працював)

LHS = ~ має бути в лапках. Нагорі, PHONE_TYPE = "SPACE TEL" також відповідатиме.


0

Використовуйте [[замість цього

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

2
Це, звичайно, неправильно. [[vs [не допомагає вимкнути логіку.
ilkkachu

0

Просто пропозиція щодо варіантів на основі рішення @ 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
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.