Considere dos expresiones condicionales expr1
y expr2
, por ejemplo, $i -eq $j
y $k -eq $l
. Podemos escribir esto de bash
varias maneras. Aquí hay dos posibilidades.
[[ expr1 || expr2 ]]
[[ expr1 ]] || [[ expr2 ]]
Estoy bastante seguro de que he visto recomendaciones aquí de que debería preferirse el segundo, pero no puedo encontrar evidencia que lo respalde.
Aquí hay un script de muestra que parece demostrar que no hay diferencia:
for i in 0 1
do
for j in 0 1
do
for k in 0 1
do
for l in 0 1
do
if [[ $i -eq $j || $k -eq $l ]]; then printf "1-yes\t"; else printf "1-no\t"; fi
if [[ $i -eq $j ]] || [[ $k -eq $l ]]; then printf "2-yes\n"; else printf "2-no\n"; fi
done
done
done
done
y salida que muestra que ambas construcciones de condiciones producen el mismo resultado:
1-yes 2-yes
1-yes 2-yes
1-yes 2-yes
1-yes 2-yes
1-yes 2-yes
1-no 2-no
1-no 2-no
1-yes 2-yes
1-yes 2-yes
1-no 2-no
1-no 2-no
1-yes 2-yes
1-yes 2-yes
1-yes 2-yes
1-yes 2-yes
1-yes 2-yes
¿Hay algún beneficio en usar una construcción sobre la otra?
Para puntos de bonificación, la misma pregunta pero generalizando a múltiples condiciones usando ||
y &&
. Por ejemplo, [[ expr1 && expr2 || expr3 ]]
.
[
otest
( no[[
), busque lasOB
etiquetas (vinculadas a la definición "obsoleta") en la especificación POSIX paratest
. Entest
, hay algunos casos patológicos donde puede ser imposible decir si(
o)
está destinado a ser significativo a la sintaxis del comando de prueba o una cadena para ser probados, para que las personas que ignoran los marcadores de obsolescencia y el uso que de sintaxis pueden realmente necesita el contrario-"x$foo"
práctica obsoleta ; con[[
eso no es un problema.Respuestas:
Creo que la recomendación que viste fue para POSIX sh y / o el
test
comando que funciona como el[
comando, en lugar de la[[
construcción que apareció en ksh (gracias Stéphane Chazelas por la sugerencia) y también se usa, por ejemplo, en bash, zsh y algunos otros conchasEn la mayoría de los lenguajes, como C, cuando ya se sabe que una cláusula es verdadera o falsa, no hay necesidad de evaluar las partes restantes dependiendo de la operación: si es verdadero no después de un lógico o lo sigue, si es falso no después de un lógico y , etc. Esto, por supuesto, permite detenerse cuando un puntero es NULL y no intentar desreferenciarlo en la siguiente cláusula.
Pero la construcción de sh
[ expr1 -o expr2 ]
(incluida la implementación de bash) no hace esto: siempre evalúa ambos lados, cuando uno quisiera que solo se evalúe expr1 . Esto podría haberse hecho por compatibilidad con latest
implementación del comando. Por otro lado, sh||
y&&
sí siguen el principio habitual: no evaluado si no cambiará el resultado.Entonces la diferencia a tener en cuenta sería:
cuyos rendimientos:
Arriba, cada uno
[
podría haber sido reemplazado por/usr/bin/[
un alias deltest
comando, que se usaba antes de[
incorporarse a los shells.Mientras que las dos siguientes construcciones:
o
Solo cederá
true
y dejaráeffect
vacío: se||
comporta correctamente y también[[
corrigió este problema.ACTUALIZAR:
Como comentó @ StéphaneChazelas, perdí varias diferencias relacionadas con la pregunta inicial. Voy a poner aquí el más importante (al menos para mí): prioridad de los operadores.
Si bien el shell no tendrá precedencia:
rendimientos (porque no hay precedencia y, por lo tanto, primero
true || true
se evalúa y luego&& false
):dentro
[[ ]]
del&&
operador tiene prioridad sobre||
:rendimientos (porque
1 -eq 1 && 1 -eq 0
está agrupado y, por lo tanto, es el segundo miembro de||
):al menos para ksh , bash , zsh .
Por lo tanto,
[[ ]]
ha mejorado el comportamiento sobre ambos[ ]
y los operadores lógicos de shell directo.fuente
[ x -o y ]
no tiene que evaluar a ambos lados. Si bien GNUtest
o el[
builtin debash
do, encontrarásstrace zsh -c '[ x -o -f /x ]'
que eso[
no intenta stat ()/x
. Lo mismo conmksh
. Pero es cierto que-o
y-a
están severamente rotos, no se pueden usar de manera confiable y son obsoletos por POSIX.[[...]]
,&&
tiene mayor precedencia que||
, mientras que fuera de ellos tienen la misma precedencia. Compareksh -c '[[ x || x && "" ]]'
vssh -c 'true || true && false'
[
/test
utilidad no tiene un==
operador. El operador de igualdad es=
(tenga en cuenta que dentro de ksh[[...]]
,=
/==
no hay operadores de igualdad, sino patrones que coinciden).&&
tiene prioridad sobre el||
interior[[...]]
(o((...))
) pero no los operadores&&
y||
shell.[[ x || y && z ]]
esx || (y && z)
whilex || y && z
is(x || y) && z
(operadores evaluados de izquierda a derecha sin precedencia). Similar a cómo*
tiene prioridad sobre+
(1 + 2 * 3
es1 + (2 * 3)
) pero+
y-
tiene la misma prioridad.&&
tiene prioridad sobre||
, entonces debería sertrue || (true && false)
, en lugar de(true || true) && false