Diferencia entre [[ ]] Y [ ] o (( )) Y ( ) en Bash

Diferencia entre [[ ]] Y [ ] o (( )) Y ( ) en Bash

¿Cuál es la diferencia entre usar [[ condition ]]and [ condition ]or (( condition ))and ( condition )? ¿En qué escenario necesitamos utilizar cualquiera de estos?

  • (( 10 > 9 ))funciona pero (( 10 -gt 9 ))no
  • [[ 10 -gt 9 ]]funciona pero [[ 10 > 9 ]]no

Respuesta1

((...))es el caparazónaritméticaconstruir. Los operadores que puede utilizar están documentados en el manual:6.5 Aritmética de Shell

(...)es unagrupamientoconstrucción que ejecuta los comandos contenidos en un subshell:3.2.4.3 Comandos de agrupación

[...]es la construcción condicional "heredada". La documentación está en6.4 Expresiones condicionales de Bash

[[...]]hace todo lo que [...]hace. La diferencia es que la división de palabras y la expansión global no se realizan para las variables internas, [[...]]por lo que citar las variables no es tan crucial. Además, [[puede hacerla coincidencia de patronescon el ==operador ycoincidencia de expresiones regularescon el =~operador.

La razón [[ 10 > 9 ]]que le da un resultado inesperado es que el >operador que está dentro [[...]]es paracomparación de cadenasy la cadena "10" es "menor que" la cadena "9".

Respuesta2

Acerca de ((…))y(…)

Como se indica enesta otra respuesta, ((…))es la construcción aritmética del shell (unabashismo) y (…)ejecuta comandos en un subshell. Son muy diferentes entre sí y de [ … ]o [[ … ]].

Por otro lado [ … ]y [[ … ]]son muy similares en casos simples; pueden estar confundidos. El resto de esta respuesta se concentra en [ … ]y [[ … ]].


nota formal

Mis objetivos aquí son:

  • para elaborar sobrediferencias y similitudes entre [ … ]y[[ … ]];
  • para proporcionar una respuesta canónica en el tema de [ … ]vs [[ … ]], de modo que las preguntas que puedan responderse con " [no es [[" se puedan vincular aquí;
  • para proporcionar una respuesta lo suficientemente exhaustiva, de modo que los usuarios ya no se sientan tentados a publicar respuestas que se centren en una única diferencia cada una.

Qué es [?

  1. [realiza una prueba.

    El propósito de [es probar algo (por ejemplo, si existe algún archivo) y devolver el estado de salida cero si la prueba tiene éxito, distinto de cero en caso contrario.

  2. [es un comando.

    [es un nombre de comando como cato echo. [(como echo) es una función incorporada en Bash, pero esto solo significa que se puede ejecutar sin generar un proceso adicional; aún se comporta como un comando. Probablemente exista un [ejecutable independiente en su sistema (p. ej. /bin/[); prueba type -a [en Bash para averiguarlo.

  3. [requiere un espacio después.

    Como comando, [toma argumentos. Como ocurre con cualquier otro comando, los argumentos deben estar separados entre sí y del nombre del comando. [fooes un nombre de comando diferente, no es equivalente a [ foo(de manera similar y más obviamente echoHello worldno lo es echo Hello world). Ciertamente necesitas un espacio (o una tabulación) después [.

  4. [requiere ].

    [requiere ]como último argumento, de lo contrario se niega a funcionar. Esto es sólo para [ … ]resaltar como un bloque, como si fuera una sintaxis especial; pero no es una sintaxis especial. Para pasar ]como último argumento necesita un espacio (o una tabulación) antes. A modo de comparación: el último argumento [ … bar]es bar]cual no es ], por lo que [ … bar]no es un comando válido.

  5. [es (casi) como test.

    [ … ]es equivalente a test …. En otras palabras, puede convertir cualquier [comando en un testcomando eliminando ]y cambiando el nombre del comando. Y puede convertir cualquier testcomando en un [comando cambiando el nombre del comando y agregando ].

  6. [no es especial.

    Es bueno recordar [que no es especial, es sólo un nombre un tanto sofisticado para un comando. Si reconoces esto, no te sorprenderá el hecho de quetodoel análisis y procesamiento del shell lo haceantesLa ejecución de un comando ( testo echo, lsetc.) también ocurre si el comando es [. En particular:

    • [no sabe si citó alguno de sus argumentos, obtiene argumentos después de que el shell elimina las comillas.

    • Sin comillas y sin escape &&o ||entre [y ]no se interpreta como un argumento para [. En otras palabras, [ … || … ]se interpreta como [ …(inválido porque no hay ]) y … ](un comando separado, sea lo que sea) conectado lógicamente con ||, exactamente como command1 || command2.

  7. [está especificado por POSIX.

    Ahí está elEspecificación POSIX para [/test. Puedes llamar [al portátil. Notas:

    • Algunas primarias son extensiones, otras están marcadas como obsoletas. Por ejemplo, nuestro código no válido ( [ … || … ]) se puede corregir como [ … -o … ], pero como -oes obsoleto, una solución mejor es [ … ] || [ … ].
    • Las implementaciones pueden respaldar más primarias. El [incorporado en Bash lo hace (ver help test); el [binario (como /bin/[) en su sistema operativo puede (ver man test).

Qué es [[? ¿En qué se [[diferencia [?

Notas:
- La pregunta está etiquetada., ahora estamos hablando de[[ en fiesta.
- Los párrafos numerados de este apartado corresponden a los párrafos numerados del apartado anterior.

  1. (semejanza)[[realiza una prueba.

    El propósito de [[es probar algo (por ejemplo, si existe algún archivo) y devolver el estado de salida cero si la prueba tiene éxito, distinto de cero en caso contrario. [[Puede probar cualquier cosa [y más.

  2. (diferencia)[[es una palabra clave.

    ​es[[ una palabra clave de shell (invoca type [[en Bash para confirmar esto). Al igual que la palabra clave [incorporada, [[pertenece a Bash; perono escomportarse como una orden.

  3. (semejanza)[[requiere un espacio después.

    (O una pestaña).

  4. (semejanza)[[requiere ]].

    [[requiere ]]más adelante en el código, sin embargo, no es solo para [[ … ]]resaltar como un bloque. Es una sintaxis especial (una diferencia, llegaremos a ella). Necesitas un espacio (o una tabulación) antes ]].

  5. (diferencia)No existe ningún comando de apariencia normal equivalente a [[.

    No hay ningún otro comando/palabra clave/lo que sea que pueda reemplazar [[de la misma manera testque puede reemplazar [.

  6. (diferencia)[[ esespecial.

    [[cambia la forma en que el shell analiza e interpreta el código, hasta el correspondiente ]]. El análisis y procesamiento normal que realiza el shell antes de ejecutar [or test(o echo, lsetc.) no se aplica dentro de [[ … ]]. En particular:

    • [[ esSepa si citó alguno de sus "argumentos". Las variables entre comillas dobles no son necesarias (aunque normalmentees), pero en algunos casos, poner una cadena (o una variable) entre comillas dobles marca la diferencia (consulteesta respuestadonde menciona "el lado derecho de =o ==o !=o =~").

    • &&o ||entre [[y ]]pertenece a la [[ … ]]construcción. [[ … || … ]]puede ser un fragmento de código válido, prácticamente equivalente a [[ … ]] || [[ … ]].

  7. (diferencia)[[no está especificado por POSIX.

    [[​no es [[Funciona en Bash, no funciona en pure sh. Es posible que otros shells lo admitan [[(por ejemplo, Zsh), pero [[pueden ser diferentes al [[de Bash.

    [[no está diseñado para cumplir con la especificación de [/ test. En muchos casos, reemplazar [con [[y ]con ]]le dará un [[ … ]]fragmento equivalente al [ … ]fragmento original; pero no siempre,a vecesEs necesario ajustar el interior. Así que no cambies [a [[ciegas. Cambiar a ciegas [[es [aún más riesgoso porque hay pruebas [[que se pueden hacer y [no se pueden hacer.


Otras notas:

  1. Ni [ni [[es parte de la if … then …sintaxis. Recuerde ifque solo prueba el estado de salida de algún código. if true; then …es válido, if sleep 5; then …es válido, de manera similar if [ … ]; then …o if [[ … ]]; then …es válido (si la parte [ … ]o [[ … ]]es en sí misma un fragmento válido).

    Dado que ((…))o también devuelve algún estado de salida (eso depende de lo que hay dentro), también (…)se puede usar después .if

  2. Personalmente prefiero utilizarlo [para cualquier prueba [que pueda realizar fácilmente, recurriendo a él [[cuando sea realmente necesario. De esta manera no me acostumbro a lo no portátil [[y sé lo que puedo hacer en un shell no tan rico como Bash.

  3. El [ejecutable de su sistema operativo no es necesariamente equivalente al [integrado en su Bash. Si [Bash lo invoca, el incorporado hará el trabajo. Si [lo invoca otra cosa, entonces el ejecutable hará el trabajo. Por ejemplo, find … -exec [ … ] …utiliza el ejecutable. No existe un [[ejecutable estándar (al menos en los sistemas que conozco), por lo que find … -exec [[ … ]] …nunca funcionará. Si hubiera un [[ejecutable, tendría que comportarse como un comando y no podría imitar la palabra clave.

Respuesta3

Hay una gran diferencia entre [ ] y [[ ]]:

a=0;
if [ $a = 1 -a $(grep ERR 1.log|wc -l) -ne 0 ];then
      echo "error";
fi 

cuando se utiliza [ ], el operador y -a no admite la evaluación de cortocircuito. En el código anterior, incluso la primera condición devuelve falso, la segunda condición aún se puede evaluar. Si el archivo 1.log no existe, se quejará "No existe tal archivo o directorio".

Pero para [[ ]]:

a=0
if [[ $a = 1 && $(grep ERR 1.log|wc -l) -ne 0 ];then
    echo "error"
fi

Incluso el archivo "1.log" no existe, estará bien porque la segunda condición no será evaluada ya que la primera condición es falsa.

Respuesta4

Habría puesto esto en un comentario, pero todavía no puedo hacerlo.

Una diferencia que he notado entre [] y [[]] es que en el primero es posible utilizar comparaciones múltiples.

La misma comparación arroja un error de sintaxis en [[]]

a=1
b=2

Esto funciona:

$  [ "$a" -gt 0 -a "$b" -gt "$a" ] && { echo '[b>a>0]'; }
[b>a>0]

Esto no funciona:

$ [[ $a -gt 0 -a $b -gt $a ]] && { echo '[[b>a>0]]'; }
-bash: syntax error in conditional expression
-bash: syntax error near `-a'

Realmente no he profundizado para ver por qué es así, pero cuando uso comparaciones múltiples, uso [].

información relacionada