Diferencia entre corchetes simples y dobles en Bash

Respuestas:

188

Las []pruebas de condición de cumplimiento posix son simples .

Double [[]]son una extensión del estándar []y son compatibles con bash y otros shells (por ejemplo, zsh, ksh). Admiten operaciones adicionales (así como las operaciones estándar posix). Por ejemplo: en ||lugar de -oy expresiones regulares que coinciden con =~. Se puede encontrar una lista más completa de diferencias en la sección del manual de bash sobre construcciones condicionales .

Úselo []siempre que desee que su secuencia de comandos sea portátil entre shells. Úselo [[]]si desea expresiones condicionales que no son compatibles []y no necesitan ser portables.

cmh
fuente
66
Agregaría que si su secuencia de comandos no comienza con un shebang que solicita explícitamente un shell compatible [[ ]](por ejemplo, bash con #!/bin/basho #!/usr/bin/env bash), debe usar la opción portátil. Las secuencias de comandos que suponen que / bin / sh admite extensiones como esta se romperán en sistemas operativos como las recientes versiones de Debian y Ubuntu, donde ese no es el caso.
Gordon Davisson
87

Diferencias de comportamiento

Probado en Bash 4.3.11:

  • Extensión POSIX vs Bash:

  • comando regular vs magia

    • [ es solo un comando regular con un nombre extraño.

      ]es solo un argumento [que evita que se utilicen más argumentos.

      Ubuntu 16.04 en realidad tiene un ejecutable /usr/bin/[proporcionado por coreutils, pero la versión integrada de bash tiene prioridad.

      Nada se altera en la forma en que Bash analiza el comando.

      En particular, <es la redirección &&y ||concatena múltiples comandos, ( )genera subcapas a menos que se escapen \y la expansión de palabras ocurre como de costumbre.

    • [[ X ]]es una construcción única que hace que Xse analice mágicamente. <, &&, ||Y ()se tratan de forma especial, y las reglas de división de palabras son diferentes.

      También hay otras diferencias como =y =~.

      En Bashese: [es un comando incorporado y [[es una palabra clave: /ubuntu/445749/whats-the-difference-between-shell-builtin-and-shell-keyword

  • <

  • && y ||

    • [[ a = a && b = b ]]: verdadero, lógico y
    • [ a = a && b = b ]: error de sintaxis, &&analizado como un separador de comando ANDcmd1 && cmd2
    • [ a = a -a b = b ]: equivalente, pero obsoleto por POSIX³
    • [ a = a ] && [ b = b ]: POSIX y equivalente confiable
  • (

    • [[ (a = a || a = b) && a = b ]]: falso
    • [ ( a = a ) ]: error de sintaxis, ()se interpreta como una subshell
    • [ \( a = a -o a = b \) -a a = b ]: equivalente, pero ()es obsoleto por POSIX
    • { [ a = a ] || [ a = b ]; } && [ a = b ]POSIX equivalente 5
  • división de palabras y generación de nombre de archivo en expansiones (split + glob)

    • x='a b'; [[ $x = 'a b' ]]: cierto, no se necesitan citas
    • x='a b'; [ $x = 'a b' ]: error de sintaxis, se expande a [ a b = 'a b' ]
    • x='*'; [ $x = 'a b' ]: error de sintaxis si hay más de un archivo en el directorio actual.
    • x='a b'; [ "$x" = 'a b' ]: Equivalente POSIX
  • =

    • [[ ab = a? ]]: cierto, porque hace coincidir patrones ( * ? [son mágicos). No se expande globalmente a los archivos en el directorio actual.
    • [ ab = a? ]: a?Glob se expande. Por lo tanto, puede ser verdadero o falso dependiendo de los archivos en el directorio actual.
    • [ ab = a\? ]: falso, no expansión glob
    • =y ==son iguales en ambos [y [[, pero ==es una extensión Bash.
    • case ab in (a?) echo match; esac: Equivalente POSIX
    • [[ ab =~ 'ab?' ]]: falso 4 , pierde magia con''
    • [[ ab? =~ 'ab?' ]]: cierto
  • =~

    • [[ ab =~ ab? ]]: verdadero, la coincidencia de expresión regular extendida POSIX , ?no se expande globalmente
    • [ a =~ a ]: error de sintaxis. Sin bash equivalente.
    • printf 'ab\n' | grep -Eq 'ab?': Equivalente POSIX (solo datos de una línea)
    • awk 'BEGIN{exit !(ARGV[1] ~ ARGV[2])}' ab 'ab?': Equivalente POSIX.

Recomendación : usar siempre [].

Hay equivalentes POSIX para cada [[ ]]construcción que he visto.

Si te [[ ]]usas:

  • perder portabilidad
  • obligar al lector a aprender las complejidades de otra extensión bash. [es solo un comando regular con un nombre extraño, no hay semántica especial involucrada.

¹ Inspirado en la [[...]]construcción equivalente en el shell Korn

² pero falla para algunos valores de ao b(like +or index) y hace una comparación numérica si ay se bparecen a enteros decimales. expr "x$a" '<' "x$b"trabaja alrededor de ambos.

³ y también falla para algunos valores de ao blike !or (.

4 en bash 3.2 y superior y la compatibilidad proporcionada a bash 3.1 no está habilitada (como con BASH_COMPAT=3.1)

5 aunque la agrupación (aquí con el {...;}grupo de comandos en lugar del (...)cual se ejecutaría una subshell innecesaria) no es necesaria ya que los operadores de shell ||y &&(en oposición a los operadores ||y && [[...]]o los operadores -o/ -a [) tienen la misma prioridad. Entonces [ a = a ] || [ a = b ] && [ a = b ]sería equivalente.

Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功
fuente
3
" Hay equivalentes POSIX para cada [[]] construcción que he visto " . Lo mismo se puede decir de cualquier lenguaje Turing Complete en la faz del planeta.
A. Rick
3
@ A.Rick que sería una respuesta válida a todas las preguntas SO "Cómo hacer X en el lenguaje Y" :-) Por supuesto, hay una "convenientemente" implícita en esa declaración.
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功
66
Fantástico resumen. Gracias por el esfuerzo. Sin embargo, no estoy de acuerdo con la recomendación. Es portabilidad versus una sintaxis más consistente y poderosa. Si puede requerir bash> 4 en sus entornos, se recomienda la sintaxis [[]].
Bernard
@Downvoters por favor explique para que pueda aprender y mejorar la información :-)
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功
8
Gran respuesta, pero creo que la recomendación : siempre[] debe leerse como Mi preferencia : úsela []si no desea perder la portabilidad . Como se indica aquí : si la portabilidad / conformidad con POSIX o BourneShell es una preocupación, se debe usar la sintaxis anterior. Si, por otro lado, el script requiere BASH, Zsh o KornShell, la nueva sintaxis suele ser más flexible, pero no necesariamente compatible con versiones anteriores. Prefiero ir [[ ab =~ ab? ]]si puedo y no me preocupa la compatibilidad con versiones anteriores queprintf 'ab' | grep -Eq 'ab?'
MauricioRobayo
14

Dentro de los corchetes individuales para la prueba de condición (es decir, [...]), algunos operadores como single =son compatibles con todos los shells, mientras que el uso del operador ==no es compatible con algunos de los shells más antiguos.

Dentro de los corchetes dobles para la prueba de condición (es decir, [...]]), no hay diferencia entre usar =o ==en conchas viejas o nuevas.

Editar: También debo tener en cuenta que: en bash, siempre use corchetes [...] dobles si es posible, porque es más seguro que los corchetes individuales. Ilustraré por qué con el siguiente ejemplo:

if [ $var == "hello" ]; then

si $ var resulta ser nulo / vacío, entonces esto es lo que ve el script:

if [ == "hello" ]; then

que romperá tu guión. La solución es usar corchetes dobles o siempre recordar poner comillas alrededor de sus variables ( "$var"). Los corchetes dobles son una mejor práctica de codificación defensiva.

sampson-chen
fuente
1
Poner comillas alrededor de todas las lecturas de variables a menos que tenga una muy buena razón para no hacerlo es una práctica de codificación defensiva mucho mejor, ya que se aplica a todas las lecturas de variables, no solo a aquellas en condiciones. Un error del instalador de iTunes eliminó una vez los archivos de las personas si el nombre del disco duro contenía espacios (o algo así). También resuelve el problema que mencionas.
Chai T. Rex
1

puede usar los corchetes dobles para la coincidencia de expresiones regulares ligeras, por ejemplo:

if [[ $1 =~ "foo.*bar" ]] ; then

(siempre que la versión de bash que esté utilizando sea compatible con esta sintaxis)

asf107
fuente
66
Excepto que ha citado el patrón, por lo que ahora se trata como una cadena literal.
ormaaj
muy cierto. a veces esto me molesta :)
asf107
1

El manual de Bash dice:

Cuando se usa con [[, los operadores '<' y '>' se ordenan lexicográficamente usando la localización actual. El comando de prueba utiliza el orden ASCII.

(El comando de prueba es idéntico a [])

usuario5500105
fuente