¿Los corchetes dobles son ​​preferibles a los corchetes individuales en Bash?

Un compañero de trabajo afirmó recientemente en una revisión de código que la construcción [[ ]] es preferible a [ ] en construcciones como

 if [ "`id -nu`" = "$someuser" ] ; then echo "I love you madly, $someuser" fi 

No pudo proporcionar un razonamiento. ¿Hay alguno?

[[ tiene menos sorpresas y generalmente es más seguro de usar. Pero no es portátil: POSIX no especifica lo que hace y solo algunas carcasas lo admiten (además de bash, escuché que ksh también lo admite). Por ejemplo, puedes hacer

 [[ -e $b ]] 

para probar si existe un archivo. Pero con [ , debe citar $b , porque divide el argumento y expande cosas como "a*" (donde [[ toma literalmente)]. Eso también tiene que ver con cómo [ puede ser un progtwig externo y recibe su argumento de manera normal, como cualquier otro progtwig (aunque también puede ser un comando integrado, pero aún así no tiene este manejo especial).

[[ también tiene otras características agradables, como la coincidencia de expresiones regulares con =~ junto con operadores como se conocen en los lenguajes tipo C). Aquí hay una buena página al respecto: ¿Cuál es la diferencia entre la prueba, [ y [[ ? y Bash Tests

[[ ]] tiene más funciones: sugiero que eche un vistazo a la Guía avanzada de scripts de Bash para obtener más información, específicamente la sección del comando de prueba extendida en el Capítulo 7. Pruebas .

Por cierto, como señala la guía, [[ ]] se introdujo en ksh88 (la versión de 1988 del shell Korn).

Diferencias de comportamiento

Primero, analicemos las diferencias de comportamiento entre ambos. Probado en Bash 4.3.11.

  • Extensión POSIX vs Bash:

    • [ es POSIX
    • [[ es una extensión de Bash
  • comando regular vs magia

    • [ es solo un comando regular con un nombre raro.

      ] es solo un argumento de [ que evita que se usen argumentos adicionales.

      Ubuntu 16.04 realmente tiene un ejecutable en /usr/bin/[ provisto por coreutils, pero la versión bash incorporada tiene prioridad.

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

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

    • [[ X ]] es una construcción única que hace que X se analice mágicamente. < , && , || y () son tratados especialmente, 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: https://askubuntu.com/questions/445749/whats-the-difference-between-shell-builtin-and-shell-keyword%5D

  • <

    • [[ a < b ]] : comparación lexicográfica
    • [ a \< b ] : lo mismo que arriba. \ required o de lo contrario redirecciona como para cualquier otro comando. Bash extensión.
    • No pude encontrar una alternativa POSIX a esto, vea: ¿Cómo probar cadenas para menor o igual?
  • && y ||

    • [[ a = a && b = b ]] : verdadero, lógico y
    • [ a = a && b = b ] : error de syntax, && analizado como un separador de comandos AND cmd1 && cmd2
    • [ a = a -ab = b ] : equivalente, pero obsoleto por POSIX
    • [ a = a ] && [ b = b ] : recomendación de POSIX
  • (

    • [[ (a = a || a = b) && a = b ]] : falso
    • [ ( a = a ) ] : error de syntax, () se interpreta como una subcapa
    • [ \( a = a -oa = b \) -aa = b ] : equivalente, pero () está en desuso por POSIX
    • ([ a = a ] || [ a = b ]) && [ a = b ] Recomendación POSIX
  • división de palabras

    • x='a b'; [[ $x = 'ab' ]] x='a b'; [[ $x = 'ab' ]] : verdadero, no se necesitan citas
    • x='a b'; [ $x = 'ab' ] x='a b'; [ $x = 'ab' ] : error de syntax, se expande a [ ab = 'ab' ]
    • x='a b'; [ "$x" = 'ab' ] x='a b'; [ "$x" = 'ab' ] : equivalente
  • =

    • [[ ab = a? ]] [[ ab = a? ]] : verdadero, porque coincide con el patrón ( * ? [ son mágicos). No se expande globalmente a los archivos en el directorio actual.
    • [ ab = a? ] [ ab = a? ] : a? glob se expande Entonces puede ser verdadero o falso dependiendo de los archivos en el directorio actual.
    • [ ab = a\? ] [ ab = a\? ] : expansión falsa, no glob
    • = y == son iguales en ambos [ y [[ , pero == es una extensión de Bash.
    • printf 'ab' | grep -Eq 'a.' : Equivalente a POSIX ERE
    • [[ ab =~ 'ab?' ]] [[ ab =~ 'ab?' ]] : falso, pierde magia con ''
    • [[ ab? =~ 'ab?' ]] [[ ab? =~ 'ab?' ]] : cierto
  • =~

    • [[ ab =~ ab? ]] [[ ab =~ ab? ]] : true, coincidencia de expresión regular extendida POSIX ,? no se expande
    • [ a =~ a ] : error de syntax
    • printf 'ab' | grep -Eq 'ab?' : Equivalente POSIX

Recomendación

Prefiero usar siempre [] .

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

Si usa [[ ]] usted:

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

¿ De qué comparador, prueba, corchete o corchete doble es el más rápido? ( http://bashcurescancer.com )

El paréntesis doble es un “comando compuesto” donde la prueba y el paréntesis son incorporados en el shell (y en realidad son el mismo comando). Por lo tanto, el corchete simple y el corchete doble ejecutan un código diferente.

La prueba y el bracket único son los más portátiles ya que existen como comandos separados y externos. Sin embargo, si está utilizando una versión remotamente moderna de BASH, se admite el doble soporte.

Una situación típica en la que no puede usar [[se encuentra en una secuencia de comandos autotools configure.ac, los paréntesis tienen un significado especial y diferente, por lo que deberá usar la test lugar de [o [[- Tenga en cuenta que la prueba y [son iguales] progtwig.

[[]] los corchetes dobles no están protegidos bajo ciertas versiones de SunOS y las declaraciones de funciones internas no están totalmente protegidas por: GNU bash, versión 2.02.0 (1) – liberación (sparc-sun-solaris2.6)

En pocas palabras, [[es mejor porque no se bifurca otro proceso. Sin corchetes o un solo soporte es más lento que un corchete doble, ya que bifurca otro proceso.