¿Cuál es la diferencia entre corchetes dobles y simples en bash?

427

Me preguntaba cuál es exactamente la diferencia entre

[[ $STRING != foo ]]

y

[ $STRING != foo ]

es decir, además de que este último es compatible con posix, se encuentra en sh y el primero es una extensión que se encuentra en bash.

0x89
fuente
1
En caso de que también se esté preguntando acerca de no usar ningún paréntesis, por ejemplo, en el contexto de una ifdeclaración, vea mywiki.wooledge.org/BashPitfalls#if_.5Bgrep_foo_myfile.5D
Kev
también, de los documentos de Ubuntu: wiki.ubuntu.com/…
radistao

Respuestas:

310

Hay varias diferencias En mi opinión, algunos de los más importantes son:

  1. [es una construcción en Bash y muchos otros proyectiles modernos. La construcción [es similar al testrequisito adicional de un cierre ]. Los integran [e testimitan la funcionalidad /bin/[y /bin/testjunto con sus limitaciones para que los scripts sean compatibles con versiones anteriores. Los ejecutables originales todavía existen principalmente para el cumplimiento de POSIX y la compatibilidad con versiones anteriores. Ejecutar el comando type [en Bash indica que [se interpreta como un valor incorporado de forma predeterminada. (Nota: which [solo busca ejecutables en la RUTA y es equivalente a type -p [)
  2. [[no es tan compatible, no necesariamente funcionará con lo que sea que /bin/shapunte. Así [[es la opción más moderna Bash / Zsh / Ksh.
  3. Debido a que [[está integrado en el shell y no tiene requisitos heredados, no necesita preocuparse por la división de palabras basada en la variable IFS para confundir las variables que se evalúan en una cadena con espacios. Por lo tanto, realmente no necesita poner la variable entre comillas dobles.

En su mayor parte, el resto es solo una sintaxis más agradable. Para ver más diferencias, recomiendo este enlace a una respuesta de preguntas frecuentes: ¿Cuál es la diferencia entre prueba, [y [[? . De hecho, si te tomas en serio las secuencias de comandos bash, te recomiendo leer el wiki completo , incluidas las preguntas frecuentes, las trampas y la guía. La sección de prueba de la sección de guía también explica estas diferencias y por qué los autores piensan que [[es una mejor opción si no necesita preocuparse por ser tan portátil. Las razones principales son:

  1. No tiene que preocuparse por citar el lado izquierdo de la prueba para que realmente se lea como una variable.
  2. No tiene que escapar menos y más que < >con barras invertidas para que no se evalúen como redireccionamiento de entrada, lo que realmente puede arruinar algunas cosas al sobrescribir los archivos. Esto vuelve a [[ser una construcción. Si [(prueba) es un programa externo, el shell tendría que hacer una excepción en la forma en que se evalúa <y >solo si /bin/testse llama, lo que realmente no tendría sentido.
Kyle Brandt
fuente
55
Gracias, el enlace a las preguntas frecuentes de bash era lo que estaba buscando (no sabía sobre esa página, gracias).
0x89
2
Edité su publicación con esta información, pero [y la prueba se ejecutan como incorporados. Los builtins fueron diseñados para reemplazar / bin / [y / bin / test pero también necesitaban reproducir las limitaciones de los binarios. El comando 'tipo [' verifica que se utiliza el incorporado. 'which [' solo busca ejecutables en la RUTA y es equivalente a 'type -P ['
klynch
133

En breve:

[es un bash Builtin

[[]] son palabras clave bash

Palabras clave: las palabras clave son muy parecidas a las incorporadas, pero la principal diferencia es que se les aplican reglas de análisis especiales. Por ejemplo, [es un bash incorporado, mientras que [[es una palabra clave bash. Ambos se usan para probar cosas, pero dado que [[es una palabra clave en lugar de una función incorporada, se beneficia de algunas reglas de análisis especiales que lo hacen mucho más fácil:

  $ [ a < b ]
 -bash: b: No such file or directory
  $ [[ a < b ]]

El primer ejemplo devuelve un error porque bash intenta redirigir el archivo b al comando [a]. El segundo ejemplo en realidad hace lo que esperas que haga. El carácter <ya no tiene su significado especial de operador Redirección de archivos.

Fuente: http://mywiki.wooledge.org/BashGuide/CommandsAndArguments

abhiomkar
fuente
3
[es un comando de shell POSIX; no necesita ser incorporado. ]es solo un argumento que ese comando busca, para que la sintaxis esté equilibrada. El comando es sinónimo de testexcepto que testno busca un cierre ].
Kaz
81

Diferencias de comportamiento

Algunas diferencias 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 para él /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: https://askubuntu.com/questions/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? ]: el a?globo 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. No hay 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 +o 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
¿Cómo usar printf 'ab' | grep -Eq 'ab?'dentro if [ … ]?
meeDamian
1
@meeDamian if ( printf 'ab' | grep -Eq 'a' ); then echo 'a'; fi. []es un comando al igual que grep. El ()puede no ser necesario en ese comando no estoy seguro: he añadido debido a la |, depende de cómo Bash analiza las cosas. Si no hubo |, estoy seguro de que puedes escribir solo if cmd arg arg; then.
Ciro Santilli 新疆 改造 中心 法轮功 六四 事件
1
@meeDamian sí, no ()parece necesario: stackoverflow.com/questions/8965509/…
Ciro Santilli 新疆 改造 中心 法轮功 六四 事件
1
Buena lista! Ver también: wiki.ubuntu.com/…
radistao
5

Single Bracket,[] es decir, es compatible con shell POSIX para encerrar una expresión condicional.

Double Brackets,[[]] es decir, es una versión mejorada (o extensión) de la versión estándar POSIX, esto es compatible con bash y otros shells (zsh, ksh).

En bash, por comparación numérica usamos eq, ne, lty gt, con soportes dobles para la comparación que podemos utilizar ==, !=, <,y >literalmente.

  • [es sinónimo de comando de prueba. Incluso si está integrado en el shell, crea un nuevo proceso.
  • [[ es una nueva versión mejorada de la misma, que es una palabra clave, no un programa.

por ejemplo:

[ var1 lt var2] #works
[ var1 < var2] #error: var2 No such file or directory 
[ var1 \< var2] #works with escape
[[ var1 < var2]] #works
Premraj
fuente
4

Basado en una lectura rápida de las secciones relevantes de la página de manual, la diferencia principal parece ser que los operadores ==y !=coinciden con un patrón, en lugar de una cadena literal, y también que existe el =~operador de comparación de expresiones regulares.

womble
fuente