¿Hay alguna diferencia importante al comparar una variable como una cadena o como un int

22

Por curiosidad, al hacer una comparación de variables bash (su valor es un integer) es posible probarlo con algún valor predefinido, ya sea declarado como into como a string.

Script de muestra :

#!/bin/bash
f1()
{
        [ "$1" == "1" ] && echo "$FUNCNAME: \"1\" compared as string"
}

f2()
{
        [[ "$1" -eq 1 ]] && echo "$FUNCNAME: \"1\" compared as int"
}

f1 $1
f2 $1

Salida :

$  ./param.sh 1
f1: "1" compared as string
f2: "1" compared as int

y

$  ./param.sh blah
$

Ambas funciones se comportan de la misma manera, por lo que me pregunto si hay una forma preferida al verificar una variable entera. Iría a comprobar intversus, intya que es más estricto, pero me pregunto si hay algún inconveniente al hacerlo string.

En este caso, f2()también es más estricto acerca de la comparación, es decir, pasar un valor decimal lo romperá, mientras f1()que no tendrá ningún problema.

fduff
fuente
Tenga en cuenta que Bash realmente no tiene un tipo de datos Integer. Básicamente, puede indicarle a Bash que trate una cadena como un entero.
helpermethod

Respuestas:

18

Sí, muchas diferencias. Por ejemplo, =verifica la igualdad de cadena exacta, pero -eqevalúa ambas expresiones aritméticamente antes de verificar la igualdad:

$ [ " 1 " -eq 1 ] && echo equal || echo not
equal
$ [ " 1 " = 1 ] && echo equal || echo not
not

$ [ +1 -eq 1 ] && echo equal || echo not
equal
$ [ +1 = 1 ] && echo equal || echo not
not

$ [ "0+1" -eq 1 ] && echo equal || echo not
equal
$ [ "0+1" = 1 ] && echo equal || echo not
not

Además, la cadena vacía pasa a ser numéricamente igual a cero:

$ [ "" -eq 0 ] && echo equal || echo not
equal
$ [ "" = 0 ] && echo equal || echo not
not

Y aparece una clase completamente diferente de diferencias cuando incorporas los operadores de comparación, considerando <vs -lt, por ejemplo:

$ [[ 2 -lt 10 ]] && echo less || echo not
less
$ [[ 2 < 10 ]] && echo less || echo not
not

Esto se debe a que la cadena "2" está alfabéticamente después de la cadena "10" (ya que 1 viene antes que 2), pero el número "2" es numéricamente menor que el número "10".

Godlygeek
fuente
2
No olvide que también hay (( ... ))para operaciones numéricas. (( " 1 " == 1 )) && echo yes || echo noresultados enyes
Patrick
7

La comparación de enteros frente a cadenas se vuelve más significativa cuando se compara mayor o menor que:

#!/bin/bash

eleven=11
nine=9

[[ $nine < $eleven ]] && echo string   # fail

[[ "$nine" -lt "$eleven" ]] && echo integer # pass

El primero falla porque el 9 viene después del 11 cuando se ordena lexicográficamente.

Tenga en cuenta que el uso de comillas no determina si está comparando cadenas o números, el operador sí. Puede agregar o eliminar las citas anteriores, no hace ninguna diferencia. Bash captura variables indefinidas entre corchetes dobles, por lo que las comillas no son necesarias. El uso de comillas con corchetes simples para las pruebas numéricas no le ahorrará ya que:

[ "" -lt 11 ]

es un error de todos modos ("se requiere una expresión entera"). Las cotizaciones son una protección efectiva con comparaciones de cadenas entre paréntesis individuales:

[ "" \< 11 ]

Nota en dobles corchetes, ""lo hará -eq 0, pero no == 0.

encerrada dorada
fuente
1
En bash, no es estrictamente necesario citar variables entre corchetes dobles: el incorporado [[es lo suficientemente inteligente como para recordar dónde están las variables, y no se dejará engañar por las variables vacías. Los corchetes simples ( [) no tienen esta característica y requieren comillas.
Glenn Jackman
@glennjackman No lo había notado. [[ -lt 11 ]]es un error, pero nothing=; [[ $nothing -lt 11 ]]no lo es. He reelaborado un poco el último párrafo.
Ricitos
2

Además de lo dicho.
Comparar la igualdad es más rápido con los números, aunque en las secuencias de comandos de shell es raro que necesite un cálculo rápido.

$ b=234
$ time for ((a=1;a<1000000;a++)); do [[ $b = "234" ]]; done

real    0m13.008s
user    0m12.677s
sys 0m0.312s

$ time for ((a=1;a<1000000;a++)); do [[ $b -eq 234 ]]; done

real    0m10.266s
user    0m9.657s
sys 0m0.572s
Emmanuel
fuente
Teniendo en cuenta que hacen cosas diferentes, diría que el rendimiento es irrelevante: debe usar el que hace lo que quiere.
godlygeek
@godlygeek La comparación de igualdad de una variable se puede lograr en ambos sentidos. "-eq" es más rápido.
Emmanuel
Prueban diferentes definiciones de igualdad. Si desea responder la pregunta "¿Esta variable contiene la cadena exacta 123", solo puede usarla =, ya que usarla -eqtambién coincidiría con "+123". Si desea saber "¿Esta variable, cuando se evalúa como una expresión aritmética, se compara igual a 123", solo puede usarla -eq. El único momento en que puedo ver dónde a un programador no le importaría qué definición de igualdad se usó es cuando sabe que el contenido de la variable está restringido a un patrón particular con anticipación.
godlygeek
@godlygeek interesante, la pregunta era sobre la comparación de la igualdad de los números como si fueran cadenas, ¿se ajusta el caso de variables limitadas de antemano a un patrón en particular?
Emmanuel
Su ejemplo ( b=234) se ajusta a ese patrón: sabe que no es +234 o "234" o "233 + 1", ya que lo asignó usted mismo, por lo que sabe que compararlo como una cadena y como un número son igualmente válidos. Pero el guión del OP, ya que toma la entrada como un argumento de línea de comando, no tiene esa restricción - considere llamarlo como ./param.sh 0+1o./param.sh " 1"
godlygeek