Usar el operador no igual para la comparación de cadenas

117

Traté de verificar si la PHONE_TYPEvariable contiene uno de los tres valores válidos.

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

El código anterior no funcionó para mí, así que intenté esto en su lugar:

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

¿Hay formas más limpias para este tipo de tarea?

munish
fuente

Respuestas:

162

Supongo que estás buscando:

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

Las reglas para estos equivalentes se llaman leyes de De Morgan y en su caso significaban:

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

Tenga en cuenta el cambio en el operador booleano o y y.

Mientras que trataste de hacer:

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

Lo que obviamente no funciona.

Nils Werner
fuente
28

Una forma mucho más corta sería:

if [[ ! $PHONE_TYPE =~ ^(NORTEL|NEC|CISCO)$ ]]; then 
  echo "Phone type must be nortel, cisco or nec."
fi
  • ^ - Para hacer coincidir un comienzo al comienzo de la línea
  • $ - Para que coincida con el final de la línea
  • =~ - Operador de comparación de expresiones regulares incorporado de Bash
0x80
fuente
2
Creo que debería serif [[ ! $PHONE_TYPE =~ ^(NORTEL|NEC|CISCO)$ ]]; then
Milan Simek
12

Buenas respuestas y una lección invaluable;) Solo quiero complementar con una nota.

El tipo de prueba que uno elija usar depende en gran medida del código, la estructura, el entorno, etc.

Una alternativa podría ser usar un interruptor o una casedeclaración como en:

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

Como segunda nota, debe tener cuidado al usar nombres de variables en mayúsculas. Esto es para evitar colisiones entre variables introducidas por el sistema, que casi siempre es todo en mayúsculas. Así en $phone_typelugar de $PHONE_TYPE.

Aunque ese es seguro, si tienes la costumbre de usar mayúsculas, algún día podrías decir IFS="boo"que estás en un mundo de dolor.

También hará que sea más fácil detectar qué es qué.

No es un tienen que pero un aspirante a considerar seriamente la posibilidad.


También es presumiblemente un buen candidato para una función. Esto hace que el código sea más fácil de leer y mantener. P.ej:

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
fuente
9

Debe usar AND, no OR.

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

o

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

Para corregir una respuesta anterior (como todavía no puedo comentar):

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

Tenga en cuenta que necesita al menos bash 4 para este uso de = ~
No funciona en bash 3.

Probé en MS Windows 7 usando bash 4.3.46 (funciona bien) y bash 3.1.17 (no funcionó)

El LHS de = ~ debe estar entre comillas. Arriba, PHONE_TYPE = "SPACE TEL" también coincidiría.

Será
fuente
0

Use [[en su lugar

if [[ "$PHONE_TYPE" != "NORTEL" ]] || [[ "$PHONE_TYPE" != "NEC" ]] || 
   [[ "$PHONE_TYPE" != "CISCO" ]]
then
echo "Phone type must be nortel,cisco or nec"
exit 1
fi
Swapnil
fuente
2
Esto, por supuesto, está mal. [[vs [no ayuda con la lógica desactivada.
ilkkachu
0

Solo una propuesta de variación basada en la solución @ 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
fuente