Operadores de igualdad de bash (==, -eq)

136

¿Alguien puede explicar la diferencia entre -eqy ==en las secuencias de comandos bash?

¿Hay alguna diferencia entre lo siguiente?

[ $a -eq $b ] y [ $a == $b ]

¿Es simplemente que ==solo se usa cuando las variables contienen números?

Victor Brunell
fuente

Respuestas:

187

Es al revés: =y ==son para comparaciones de cadenas, -eqes para las numéricas. -eqestá en la misma familia que -lt, -le, -gt, -ge, y -ne, si eso ayuda a recordar cuál es cuál.

==es un bash-ism, por cierto. Es mejor usar POSIX =. En bash, los dos son equivalentes, y en sh simple =es el único garantizado para funcionar.

$ a=foo
$ [ "$a" = foo ]; echo "$?"       # POSIX sh
0
$ [ "$a" == foo ]; echo "$?"      # bash specific
0
$ [ "$a" -eq foo ]; echo "$?"     # wrong
-bash: [: foo: integer expression expected
2

(Nota al margen: ¡Cite esas expansiones variables! No omita las comillas dobles anteriores).

Si está escribiendo una #!/bin/bashsecuencia de comandos, le recomiendo usar en su [[lugar . La forma duplicada tiene más características, más sintaxis natural y menos problemas que lo harán tropezar. Ya no se requieren comillas dobles $a, por ejemplo:

$ [[ $a == foo ]]; echo "$?"      # bash specific
0

Ver también:

John Kugelman
fuente
1
@DJCrashdummy, [[en su conjunto es un ksh-ism de la era de 1980 que bash (y muchos otros shells) adoptaron. Ese es el punto - si usted tiene [[ en absoluto , entonces se puede asumir con seguridad que todas las extensiones KSH implementados alrededor de él (fnmatch() patrón al estilo de juego, las expresiones regulares con ERE =~, y sí, la supresión de la cadena de la división y el englobamiento sistema de archivos) serán disponible. Dado que la [[sintaxis no es POSIX en su totalidad, no hay pérdida de portabilidad adicional al suponer que las funciones con las que nació estarán disponibles.
Charles Duffy
1
@DJCrashdummy, ... el enlace que muestra señala correctamente que a veces se necesitan citas en el lado derecho de [[ $var = $pattern ]], si desea que lo que de otro modo se interpretaría como un patrón fnmatch se interprete como una cadena literal. La cadena foono tiene interpretación no literal, lo que hace que dejar las comillas sea completamente seguro; es solo si el OP quería coincidir, digamos, foo*(con el asterisco como literal, no significando nada que pueda venir después de la cadena foo) que se necesitarían comillas o escape.
Charles Duffy
@CharlesDuffy desafortunadamente no tengo idea de a qué te refieres, porque mi comentario parece haber sido eliminado y no puedo recordarlo porque ha pasado bastante tiempo. :-(
DJCrashdummy
@DJCrashdummy, ... eh, supuse que lo habías retirado tú mismo. Básicamente, fue una objeción sobre las expansiones no [[citadas dentro de ser un bashism (lo que indica que esa fue la única razón por la que no estaba dando la respuesta a +1).
Charles Duffy
@CharlesDuffy no, no es la única razón: además del hecho de que no me gustan bash-isms, ksh-isms, etc. para tareas simples (solo causa problemas y confunde a los principiantes), no entiendo por qué mezclar solo llaves [ ... ]y doble signo igual ==. : - /
DJCrashdummy
27

Depende de la construcción de prueba alrededor del operador. Sus opciones son paréntesis dobles, llaves dobles, llaves simples o prueba

Si usa ((...)) , está probando la equidad aritmética con ==como en C:

$ (( 1==1 )); echo $?
0
$ (( 1==2 )); echo $?
1

(Nota: 0significa trueen el sentido de Unix y no cero es una prueba fallida)

Usar -eqdentro del paréntesis doble es un error de sintaxis.

Si estas usando [...] (o llave simple) o [...] (o llave doble), o testpuede usar uno de -eq, -ne, -lt, -le, -gt, o -ge como una comparación aritmética .

$ [ 1 -eq 1 ]; echo $?
0
$ [ 1 -eq 2 ]; echo $?
1
$ test 1 -eq 1; echo $?
0

El ==interior de llaves simples o dobles (o testcomando) es uno de los operadores de comparación de cadenas :

$ [[ "abc" == "abc" ]]; echo $?
0
$ [[ "abc" == "ABC" ]]; echo $?
1

Como operador de cadena, =es equivalente ==y nota el espacio en blanco alrededor =o ==es necesario.

Si bien puede hacer [[ 1 == 1 ]]o [[ $(( 1+1 )) == 2 ]]está probando la igualdad de la cadena, no la igualdad aritmética.

Entonces -eq el resultado probablemente se esperaba que el valor entero de 1+1sea ​​igual a 2pesar de que RH es una cadena y tiene un espacio final:

$ [[ $(( 1+1 )) -eq  "2 " ]]; echo $?
0

Mientras que una comparación de cadenas de la misma recoge el espacio final y, por lo tanto, la comparación de cadenas falla:

$ [[ $(( 1+1 )) ==  "2 " ]]; echo $?
1

Y una comparación de cadenas errónea puede producir la respuesta incorrecta completa. '10' es lexicográficamente menor que '2', por lo que una comparación de cadenas devuelve trueo 0. Muchos son mordidos por este error:

$ [[ 10 < 2 ]]; echo $?
0

vs la prueba correcta para ser 10 aritméticamente menor que 2:

$ [[ 10 -lt 2 ]]; echo $?
1

En los comentarios, hay una cuestión de razón técnica que usa el entero -eqen cadenas devuelve True para cadenas que no son lo mismo:

$ [[ "yes" -eq "no" ]]; echo $?
0

La razón es que Bash no está tipificado . los-eq hace que las cadenas se interpreten como enteros si es posible, incluida la conversión de base:

$ [[ "0x10" -eq 16 ]]; echo $?
0
$ [[ "010" -eq 8 ]]; echo $?
0
$ [[ "100" -eq 100 ]]; echo $?
0

Y 0 si Bash piensa que es solo una cadena:

$ [[ "yes" -eq 0 ]]; echo $?
0
$ [[ "yes" -eq 1 ]]; echo $?
1

Entonces [[ "yes" -eq "no" ]]es equivalente a[[ 0 -eq 0 ]]

Última nota: muchas de las extensiones específicas de Bash para las construcciones de prueba no son POSIX y, por lo tanto, fallarán en otros shells. Otros proyectiles generalmente no son compatibles [[...]]y ((...))o== .

perro
fuente
Tengo curiosidad sobre la razón técnica para [[ "yes" -eq "no" ]]devolver True. ¿Cómo bash coacciona estas cadenas a valores enteros que se pueden comparar? ;-)
odony
44
Las variables Bash no están tipificadas, por lo que [[ "yes" -eq "no" ]]es equivalente a [[ "yes" -eq 0 ]] o [[ "yes" -eq "any_noninteger_string" ]]: todo es verdadero. La -eqcomparación de enteros de fuerzas. El "yes"se interpreta como un entero 0; la comparación es Verdadera si el otro entero es 0o el resultado de la cadena es 0.
dawg
Boo, hiss re: muestra (no portátil) ==en los ejemplos de código y solo menciona (portátil, estandarizado) =debajo.
Charles Duffy el
24

==es un alias específico de bash para =y realiza una comparación de cadena (léxica) en lugar de una comparación numérica. eqsiendo una comparación numérica, por supuesto.

Finalmente, generalmente prefiero usar el formulario if [ "$a" == "$b" ]

Elliott Frisch
fuente
15
Usar ==aquí es una mala forma, ya que solo =POSIX lo especifica.
Charles Duffy
9
Si realmente insiste en usarlo ==, póngalo entre [[y ]]. (Y asegúrese de que la primera línea de su script especifique el uso /bin/bash).
holgero
18

Chicos: Varias respuestas muestran ejemplos peligrosos. El ejemplo de OP [ $a == $b ]utilizó específicamente la sustitución de variables sin comillas (a partir de la edición de octubre '17). Para [...]eso es seguro para la igualdad de cadena.

Pero si va a enumerar alternativas como [[...]], debe informar también que se debe citar el lado derecho. Si no se cita, ¡es una coincidencia de patrón! (De la página de manual de bash: "Cualquier parte del patrón puede ser citada para forzar que coincida como una cadena").

Aquí en bash, las dos declaraciones que arrojan "sí" son coincidencia de patrones, otras tres son igualdad de cadena:

$ rht="A*"
$ lft="AB"
$ [ $lft = $rht ] && echo yes
$ [ $lft == $rht ] && echo yes
$ [[ $lft = $rht ]] && echo yes
yes
$ [[ $lft == $rht ]] && echo yes
yes
$ [[ $lft == "$rht" ]] && echo yes
$
bk-se
fuente
2
Debe estar [ "$lht" = "$rht" ] entre comillas para ser confiable incluso para la igualdad. Si tiene un archivo creado con touch 'Afoo -o AB', [ $lft = $rht ]devolverá verdadero, a pesar de que ese nombre de archivo no es idéntico en absolutoAB .
Charles Duffy, el