¿Cuál es la diferencia entre los operadores Bash [[vs [vs (vs ((?

Estoy un poco confundido sobre qué hacen estos operadores de manera diferente cuando usado en bash (corchetes, corchetes dobles, paréntesis y paréntesis doble).

[[ , [ , ( , (( 

He visto a personas usarlos en declaraciones if como esta:

if [[condition]] if [condition] if ((condition)) if (condition) 

Comentarios

  • Relacionado: usando corchetes simples o dobles – bash
  • Los paréntesis y los corchetes no son ‘ t tan fáciles de buscar en la documentación, y ‘ s todo lo que tiene si no ‘ no conoce los nombres de esas funciones.

Respuesta

En shells similares a Bourne, una instrucción if generalmente se ve como

if command-list1 then command-list2 else command-list3 fi 

La cláusula then se ejecuta si el código de salida del lista de comandos es cero. Si el código de salida es distinto de cero, entonces se ejecuta la cláusula else. command-list1 puede ser simple o complejo. Puede ser, por ejemplo, una secuencia de una o más canalizaciones separadas por uno de los operadores ;, &, &&, || o nueva línea. Las if condiciones que se muestran a continuación son solo casos especiales de command-list1:

  1. if [ condition ]

    [ es otro nombre para el comando tradicional test. [ / test es una utilidad POSIX estándar. Todos los shells POSIX lo tienen incorporado (aunque POSIX² no lo requiere). El comando test establece un código de salida y la instrucción if actúa en consecuencia. Las pruebas típicas son si un archivo existe o un número es igual a otro.

  2. if [[ condition ]]

    Esta es una nueva variación actualizada de test ¹ de ksh que bash , zsh , yash , busybox sh también son compatibles. Esta construcción [[ ... ]] también establece un código de salida y la if actúa en consecuencia. Entre sus características extendidas, puede probar si una cadena coincide con un patrón de comodín (no en busybox sh ).

  3. if ((condition))

    Otra extensión ksh que también admiten bash y zsh . Esto realiza aritmética. Como resultado de la aritmética, se establece un código de salida y la instrucción if actúa como rdingly. Devuelve un código de salida de cero (verdadero) si el resultado del cálculo aritmético es distinto de cero. Como [[...]], este formulario no es POSIX y, por lo tanto, no es portátil.

  4. if (command)

    Esto ejecuta el comando en un subshell. Cuando el comando se completa, establece un código de salida y la instrucción if actúa en consecuencia.

    Una razón típica para usar una subcapa como esta es limitar los efectos secundarios de command si command requirió asignaciones de variables u otros cambios en el entorno del shell. Estos cambios no permanecen después de que se completa el subshell.

  5. if command

    El comando se ejecuta y la instrucción if actúa de acuerdo con su código de salida.


¹ aunque no es realmente un comando sino una construcción de shell especial con su propia sintaxis separada de la del comando normal, y variar significativamente entre las implementaciones de shell

² POSIX requiere que haya un test y [ utilidades en el sistema sin embargo, aunque en el caso de [, se sabe que varias distribuciones de Linux son m emitirlo.

Comentarios

  • Gracias por incluir la quinta opción. Esa ‘ es la clave para comprender cómo funciona realmente y está sorprendentemente infrautilizada.
  • Tenga en cuenta que [ es en realidad un binario, no un comando o símbolo interno. Generalmente vive en /bin.
  • @JulienR. en realidad, [ es una función integrada, al igual que test. Hay versiones binarias disponibles por razones de compatibilidad. Consulte help [ y help test.
  • Vale la pena señalar que mientras ((no es POSIX, $(( es decir, la expansión aritmética es y ‘ s fácil de confundir. A menudo, una solución alternativa es usar algo como [ $((2+2)) -eq 4 ] para hacer uso de la aritmética en declaraciones condicionales
  • Me gustaría poder votar a favor esta respuesta más de una vez. Explicación perfecta.

Respuesta

  • (…) Los paréntesis indican un subshell . Lo que hay dentro de ellos no es una expresión como en muchos otros idiomas. Es una lista de comandos (como fuera de paréntesis). Estos comandos se ejecutan en un subproceso separado, por lo que cualquier redirección, asignación, etc. realizada dentro del paréntesis no tiene efecto fuera del paréntesis.
    • Con un signo de dólar, $(…) es una sustitución de comando : hay un comando entre paréntesis y el resultado del comando se usa como parte de la línea de comando (después de expansiones adicionales a menos que la sustitución sea entre comillas dobles, pero eso «s otra historia ).
  • { … } Las llaves son como paréntesis en el sentido de que agrupan comandos, pero solo influyen en el análisis, no en la agrupación. El programa x=2; { x=4; }; echo $x imprime 4, mientras que x=2; (x=4); echo $x imprime 2. (También las llaves que son palabras clave deben estar delimitadas y se encuentra en la posición de comando (de ahí el espacio después de { y el ; antes de }) mientras que los paréntesis no «t. Eso es solo una peculiaridad de sintaxis.)
    • Con un signo de dólar al principio, ${VAR} es un expansión de parámetros , expandiéndose al valor de una variable, con posibles transformaciones adicionales. El shell ksh93 también admite ${ cmd;} como forma de sustitución de comandos que no genera una subcapa.
  • ((…)) paréntesis dobles rodean una instrucción aritmética , es decir, un cálculo en números enteros, con una sintaxis que se asemeja a otros lenguajes de programación. Esta sintaxis se usa principalmente para asignaciones y en condicionales. Solo existe en ksh / bash / zsh, no en sh simple.
    • La misma sintaxis se usa en expresiones aritméticas $((…)), que se expanden al valor entero de la expresión.
  • [ … ] los corchetes simples rodean expresiones condicionales . Las expresiones condicionales se basan principalmente en operadores como -n "$variable" para probar si una variable está vacía y -e "$file" para probar si existe un archivo. Tenga en cuenta que necesita un espacio alrededor de cada operador (p. ej. [ "$x" = "$y" ], no [ "$x"="$y" ] ), y un espacio o un carácter como ; tanto dentro como fuera de los corchetes (por ejemplo, [ -n "$foo" ], no [-n "$foo"] ).
  • [[ … ]] los corchetes dobles son una forma alternativa de expresiones condicionales en ksh / bash / zsh con algunas características adicionales, por ejemplo, puede escribir [[ -L $file && -f $file ]] para probar si un archivo es un enlace simbólico a un archivo normal, mientras que los corchetes simples requieren [ -L "$file" ] && [ -f "$file" ]. Consulte ¿Por qué la expansión de parámetros con espacios sin comillas funciona entre corchetes dobles [[pero no corchetes simples [? para obtener más información sobre este tema.

En el shell, cada comando es un comando condicional: cada comando tiene un estado de retorno que es 0 indicando éxito o un número entero entre 1 y 255 (y potencialmente más en algunos shells) indicando falla. El [ … ] comando (o [[ … ]] forma de sintaxis) es un comando particular que también se puede escribir test … y tiene éxito cuando existe un archivo, o cuando una cadena no está vacía, o cuando un número es más pequeño que otro, etc. La forma de sintaxis ((…)) tiene éxito cuando un número es distinto de cero .Aquí hay algunos ejemplos de condicionales en un script de shell:

  • Pruebe si myfile contiene la cadena hello:

    if grep -q hello myfile; then … 
  • Si mydir es un directorio, cambie a y hacer cosas:

    if cd mydir; then echo "Creating mydir/myfile" echo "some content" >myfile else echo >&2 "Fatal error. This script requires mydir to exist." fi 
  • Pruebe si hay un archivo llamado myfile en el directorio actual:

    if [ -e myfile ]; then … 
  • Lo mismo, pero también incluye enlaces simbólicos colgantes:

    if [ -e myfile ] || [ -L myfile ]; then … 
  • Pruebe si el valor de x (que se supone que es numérico) es al menos 2, portablemente:

    if [ "$x" -ge 2 ]; then … 
  • Pruebe si el valor de x (que se supone que es numérico) es al menos 2, en bash / ksh / zsh:

    if ((x >= 2)); then … 

Comentarios

  • Tenga en cuenta que un solo corchete admite -a en lugar de &&, por lo que se puede escribir: [ -L $file -a -f $file ], que es el mismo número de caracteres entre paréntesis sin los [ y ]
  • @AlexisWilke Los operadores -a y -o son problemático porque pueden conducir a análisis incorrectos si algunos de los operandos involucrados parecen operadores. Esa ‘ es la razón por la que no ‘ los menciono: no tienen ninguna ventaja y no ‘ t siempre funciona. Y nunca escriba expansiones de variables sin comillas sin una buena razón: [[ -L $file -a -f $file ]] está bien, pero con corchetes simples necesita [ -L "$file" -a -f "$file" ] (que está bien, por ejemplo, si $file siempre comienza con / o ./).
  • Tenga en cuenta que it ‘ s [[ -L $file && -f $file ]] (no -a con [[...]] variante).

Respuesta

[ vs [[

Esta respuesta cubrirá el [ vs [[ subconjunto de la pregunta.

Algunas diferencias en Bash 4.3.11:

  • Extensión POSIX vs Bash:

  • comando regular vs magia

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

      ] es solo el último argumento de [.

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

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

    En particular, < es redirección, && y || concatenan varios comandos, ( ) genera subcapas a menos que \ se escape, y la expansión de palabras ocurre como de costumbre.

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

      También hay más diferencias como = y =~ .

    En Bashese: [ es un comando integrado 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 AND cmd1 && cmd2
    • [ a = a ] && [ b = b ]: equivalente confiable de POSIX
    • [ a = a -a b = b ]: casi equivalente, pero en desuso por POSIX porque es una locura y falla para algunos valores de a o b como ! o ( que se interpretarían como operaciones lógicas
  • (

    • [[ (a = a || a = b) && a = b ]]: falso. Sin ( ), sería verdadero porque [[ && ]] tiene mayor precedencia que [[ || ]]
    • [ ( a = a ) ]: error de sintaxis, () se interpreta como una subcapa
    • [ \( a = a -o a = b \) -a a = b ]: equivalente, pero (), -a y -o están en desuso por POSIX. Sin \( \) sería cierto porque -a tiene mayor precedencia que -o
    • { [ a = a ] || [ a = b ]; } && [ a = b ] equivalente POSIX no obsoleto. Sin embargo, en este caso particular, podríamos haber escrito simplemente: [ a = a ] || [ a = b ] && [ a = b ] porque || y && los operadores de shell tienen la misma precedencia a diferencia de [[ || ]] y [[ && ]] y -o, -a y [
  • división de palabras y generación de nombre de archivo en expansiones (split + glob )

    • x="a b"; [[ $x = "a b" ]]: verdadero, no se necesitan comillas
    • x="a b"; [ $x = "a b" ]: sintaxis error, 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 a POSIX
  • =

    • [[ ab = a? ]]: verdadero, porque hace coincidencia de patrones ( * ? [ son mágicos). No se expande glob af archivos en el directorio actual.
    • [ ab = a? ]: a? glob se expande. Entonces puede ser verdadero o falso dependiendo de los archivos en el directorio actual.
    • [ ab = a\? ]: falso, no expansión global
    • = y == son iguales en [ y [[, pero == es una extensión de Bash.
    • case ab in (a?) echo match; esac: equivalente a POSIX
    • [[ ab =~ "ab?" ]]: falso, pierde magia con "" en Bash 3.2 y superior y la compatibilidad proporcionada con bash 3.1 no está habilitada (como con BASH_COMPAT=3.1)
    • [[ ab? =~ "ab?" ]]: verdadero
  • =~

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

Recomendación: utilice siempre []

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

Si usa [[ ]] usted:

  • pierde portabilidad
  • obliga 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.

Gracias a Stéphane Chazelas para correcciones y adiciones importantes.

Comentarios

  • @St é phaneChazelas gracias por la información. ‘ he añadido expr a la respuesta. El término » Bash ex tension » no significa que Bash fue el primer shell en agregar algo de sintaxis, aprender POSIX sh vs Bash ya es suficiente para volverme loco.
  • Ver man test si probó man [ y se perdió. Eso explicará la variante POSIX.
  • Corrección: ] es un argumento para el comando [, pero no ‘ t evitar que se utilicen más argumentos. ] debe ser el último argumento de [, pero también puede aparecer como parte de la expresión de prueba. Por ejemplo, if [ "$foo" = ] ]; then probará si la variable foo está configurada en «] » (al igual que if [ ] = "$foo" ]; then).
  • @GordonDavisson gracias, no ‘ t sé eso, arreglado.
  • @ tgm1024 – Monica fue maltratada, sí, esa también es una consideración válida.

Respuesta

De la documentación de bash :

La lista (list) se ejecuta en un entorno de subshell (consulte ENTORNO DE EJECUCIÓN DE COMANDOS a continuación). Las asignaciones de variables y los comandos integrados que afectan el entorno del shell no permanecen en vigor después de que se completa el comando. El estado de retorno es el estado de salida de la lista.

En otras palabras, te aseguras de que lo que suceda en la «lista» (como una cd) no tenga ningún efecto fuera de la ( y ). Lo único que se filtrará es el código de salida del último comando o con set -e el primer comando que genera un error (otro que unos pocos, como if, while, etc.)

((expression)) La expresión se evalúa de acuerdo con las reglas que se describen a continuación en EVALUACIÓN ARITMÉTICA. Si el valor de la expresión no es cero, el estado de retorno es 0; de lo contrario, el estado de retorno es 1. Esto es exactamente equivalente a let » expresión «.

Esta es una extensión de bash que te permite hacer matemáticas. Esto es algo similar a usar expr sin todas las limitaciones de expr (como tener espacios en todas partes, escapar de *, etc.)

[[ expression ]] Devuelve un estado de 0 o 1 dependiendo de evaluación de la expresión condicional expresión. Las expresiones se componen de las primarias que se describen a continuación en EXPRESIONES CONDICIONALES. La división de palabras y la expansión de nombre de ruta no se realizan en las palabras entre [[y]]; Se realizan expansión de tilde, expansión de parámetros y variables, expansión aritmética, sustitución de comandos, sustitución de procesos y eliminación de comillas. Los operadores condicionales como -f deben estar sin comillas para ser reconocidos como primarios.

Cuando se usan con [[, el < y > los operadores ordenan lexicográficamente usando la configuración regional actual.

Esto ofrece una prueba avanzada para comparar cadenas, números y archivos un poco como test ofertas, pero más poderosas.

[ expr ] Devuelve un estado de 0 (verdadero) o 1 (falso) dependiendo de la evaluación de la expresión condicional expr. Cada operador y operación debe ser un argumento separado. Las expresiones se componen de las primarias descritas anteriormente en EXPRESIONES CONDICIONALES. test no acepta ninguna opción, ni acepta ni ignora un argumento de – como significando el final de las opciones.

[…]

Este llama a test. En realidad, en los viejos tiempos, [ era un enlace simbólico a test. Funciona de la misma manera y tiene las mismas limitaciones. Dado que un binario conoce el nombre con el que se inició, el programa de prueba sabe cuándo se inició como [ y puede ignorar su último parámetro, que se espera que sea ]. Trucos divertidos de Unix.

Tenga en cuenta que en el caso de bash, [ y test son funciones integradas (como se menciona en un comentario), pero se aplican prácticamente las mismas limitaciones.

Comentarios

  • Aunque test y [ son, por supuesto, comandos integrados en Bash, pero ‘ es probable que un El binario externo también existe.
  • El binario externo para [ no es un enlace simbólico a test en la mayoría de los sistemas modernos .
  • De alguna manera, me parece divertido que se molesten en crear dos binarios separados, que tienen exactamente lo que necesitan, en lugar de simplemente combinarlos y agregar un par de condicionales. Aunque en realidad strings /usr/bin/test muestra que también tiene el texto de ayuda, así que no ‘ no sé qué decir.
  • @ Random832 Entiendo su punto sobre la lógica de GNU para evitar un comportamiento arg0 inesperado, pero sobre los requisitos POSIX, yo no ‘ t sería tan afirmativo. Si bien el comando test obviamente se requiere que exista como un comando independiente basado en archivo por el estándar, nada en él indica que su [ variante debe ser implementado de esa manera también. Por ejemplo, Solaris 11 no ‘ t proporciona ningún [ ejecutable pero, no obstante, es totalmente compatible con los estándares POSIX
  • (salida 1) tiene un efecto fuera del paréntesis.

Respuesta

Algunos ejemplos:

Prueba tradicional:

foo="some thing" # check if value of foo is not empty if [ -n "$foo" ] ; then... if test -n "$foo" ; then... 

test y [ son comandos como cualquier otro, por lo que la variable se divide en palabras a menos que esté entre comillas.

Prueba de estilo nuevo

[[ ... ]] es una (más reciente) construcción de shell especial, que funciona de manera un poco diferente, lo más obvio es que no tiene variables de división de palabras:

if [[ -n $foo ]] ; then... 

Algunos documentación sobre [ y [[ aquí .

Prueba aritmética:

foo=12 bar=3 if (( $foo + $bar == 15 )) ; then ... 

«Normal «comandos:

Todos los anteriores actúan como comandos normales, y if puede aceptar cualquier comando:

# grep returns true if it finds something if grep pattern file ; then ... 

Varios comandos:

O podemos usar varios comandos. Envolver un conjunto de comandos en ( ... ) los ejecuta en el subshell, creando una copia temporal del estado del shell (directorio de trabajo, variables). Si es necesario para ejecutar algún programa temporalmente en otro directorio:

# this will move to $somedir only for the duration of the subshell if ( cd $somedir ; some_test ) ; then ... # while here, the rest of the script will see the new working # directory, even after the test if cd $somedir ; some_test ; then ... 

Responder

Comandos de agrupación

Bash proporciona dos formas de agrupar una lista de comandos que se ejecutarán como una unidad.

( list ) Al colocar una lista de comandos entre paréntesis, se crea un entorno de subcapa y cada uno de los comandos de la lista se ejecuta en esa subcapa. Dado que la lista es ejecutado en una subcapa, las asignaciones de variables no permanecen en efecto después de que se completa la subcapa.

$ a=1; (a=2; echo "inside: a=$a"); echo "outside: a=$a" inside: a=2 outside: a=1 

{ list; } Colocando una La lista de comandos entre llaves hace que la lista se ejecute en el contexto de shell actual . No se crea ninguna subcapa. Se requiere la siguiente lista con punto y coma (o nueva línea). Fuente

${} Parameter expansion Ex: ANIMAL=duck; echo One $ANIMAL, two ${ANIMAL}s $() Command substitution Ex: result=$(COMMAND) $(()) Arithmetic expansion Ex: var=$(( 20 + 5 )) 

Construcciones condicionales

Paréntesis simple ie []
Para comparación ==, !=, <, y > y deben usarse y para la comparación numérica eq, ne,lt y gt debe usarse.

Corchetes mejorados ie [[]]

En todos los ejemplos anteriores, usamos solo corchetes simples para encerrar la expresión condicional, pero bash permite corchetes dobles que sirven como una versión mejorada de la sintaxis de corchetes simples.

Para comparar ==, !=, <, y > se pueden usar literalmente.

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

Fuente

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *