Cómo usar llaves dobles o simples, paréntesis, llaves

658

Estoy confundido por el uso de corchetes, paréntesis, llaves en Bash, así como por la diferencia entre sus formas dobles o simples. ¿Hay una explicación clara?

Tim
fuente

Respuestas:

606

En Bash, testy [son conchas incorporadas.

El paréntesis doble , que es una palabra clave de shell, permite una funcionalidad adicional. Por ejemplo, puede utilizar &&y ||en lugar de -ay -oy hay un operador de expresión regular coincidente =~.

Además, en una prueba simple, los corchetes dobles parecen evaluarse mucho más rápido que los simples.

$ time for ((i=0; i<10000000; i++)); do [[ "$i" = 1000 ]]; done

real    0m24.548s
user    0m24.337s
sys 0m0.036s
$ time for ((i=0; i<10000000; i++)); do [ "$i" = 1000 ]; done

real    0m33.478s
user    0m33.478s
sys 0m0.000s

Las llaves, además de delimitar un nombre de variable, se usan para la expansión de parámetros, por lo que puede hacer cosas como:

  • Truncar el contenido de una variable

    $ var="abcde"; echo ${var%d*}
    abc
    
  • Hacer sustituciones similares a sed

    $ var="abcde"; echo ${var/de/12}
    abc12
    
  • Use un valor predeterminado

    $ default="hello"; unset var; echo ${var:-$default}
    hello
    
  • y varios mas

Además, las expansiones de llaves crean listas de cadenas que generalmente se repiten en bucles:

$ echo f{oo,ee,a}d
food feed fad

$ mv error.log{,.OLD}
(error.log is renamed to error.log.OLD because the brace expression
expands to "mv error.log error.log.OLD")

$ for num in {000..2}; do echo "$num"; done
000
001
002

$ echo {00..8..2}
00 02 04 06 08

$ echo {D..T..4}
D H L P T

Tenga en cuenta que las características iniciales de incremento e cero no estaban disponibles antes de Bash 4.

Gracias a gboffi por recordarme sobre expansiones de aparatos ortopédicos.

Los paréntesis dobles se usan para operaciones aritméticas :

((a++))

((meaning = 42))

for ((i=0; i<10; i++))

echo $((a + b + (14 * c)))

y le permiten omitir los signos de dólar en las variables de enteros y matrices e incluyen espacios alrededor de los operadores para facilitar la lectura.

Los corchetes individuales también se usan para los índices de matriz :

array[4]="hello"

element=${array[index]}

Se requieren llaves para las referencias de matriz (¿la mayoría / todas?) En el lado derecho.

El comentario de ephemient me recordó que los paréntesis también se usan para subcapas. Y que se usan para crear matrices.

array=(1 2 3)
echo ${array[1]}
2
Pausado hasta nuevo aviso.
fuente
8
ADVERTENCIA: Esa función es una bomba de horquilla, no la ejecutes. Ver: en.wikipedia.org/wiki/Fork_bomb
Pausado hasta nuevo aviso.
3
Es solo una bomba tenedor si la invocas con un adicional :.
Ephemient
77
También para completar, acabo de encontrar esto en un viejo script $[expression]:; esta es la sintaxis de expresión aritmética antigua y obsoleta para la sintaxis preferida más nueva:$((expression))
michael
2
@DennisWilliamson Otro uso de llaves en la bashcreación de secuencias, como se menciona periféricamente a continuación ( stackoverflow.com/a/8552128/2749397 ) Como me gustaría comentar un poco esta característica (ya que no lo mencionaste ;-) I ' Me estoy tomando la libertad de usar la respuesta más votada como vehículo ... Dos ejemplos de literales de secuencia: echo {01..12}-> 01 02 03 04 05 06 07 08 09 10 11 12(observe el cero inicial); echo {C..Q}-> C D E F G H I J K L M N O P Q. Su uso principal es en bucles, por ejemplo, for cnt in {01..12} ; do ... ${cnt} ... ; done
gboffi
1
@gboffi: La función de relleno cero estuvo disponible en Bash 4. Además, en Bash 4, puede especificar un incremento en una secuencia: echo {01..12..2}-> "01 03 05 07 09 11". Gracias por el recordatorio sobre las secuencias. Lo agregaré a mi respuesta.
Pausado hasta nuevo aviso.
336
  1. Un solo paréntesis ( [) generalmente llama a un programa llamado [; man testo man [para más información Ejemplo:

    $ VARIABLE=abcdef
    $ if [ $VARIABLE == abcdef ] ; then echo yes ; else echo no ; fi
    yes
    
  2. El paréntesis doble ( [[) hace lo mismo (básicamente) que un paréntesis simple, pero es un bash incorporado.

    $ VARIABLE=abcdef
    $ if [[ $VARIABLE == 123456 ]] ; then echo yes ; else echo no ; fi
    no
    
  3. Los paréntesis ( ()) se usan para crear una subshell. Por ejemplo:

    $ pwd
    /home/user 
    $ (cd /tmp; pwd)
    /tmp
    $ pwd
    /home/user
    

    Como puede ver, el subshell le permitió realizar operaciones sin afectar el entorno del shell actual.

  4. (a) Las llaves ( {}) se utilizan para identificar inequívocamente variables. Ejemplo:

    $ VARIABLE=abcdef
    $ echo Variable: $VARIABLE
    Variable: abcdef
    $ echo Variable: $VARIABLE123456
    Variable:
    $ echo Variable: ${VARIABLE}123456
    Variable: abcdef123456
    

    (b) Las llaves también se utilizan para ejecutar una secuencia de comandos en el contexto actual del shell, por ejemplo

    $ { date; top -b -n1 | head ; } >logfile 
    # 'date' and 'top' output are concatenated, 
    # could be useful sometimes to hunt for a top loader )
    
    $ { date; make 2>&1; date; } | tee logfile
    # now we can calculate the duration of a build from the logfile
    

Sin ( )embargo, hay una sutil diferencia sintáctica con (ver referencia de bash ); en esencia, un punto y coma ;después de la última orden entre llaves es una necesidad, y las llaves {, } debe estar rodeado por espacios.

Carl Norum
fuente
26
Bueno, en [realidad es una función integrada en Bash, pero se supone que debe actuar /bin/[como algo opuesto a la función [[integrada. [[tiene características diferentes, como operaciones más lógicas y diferentes roles de citas. Además: los paréntesis simples también se usan para matrices, sustitución de procesos y globos extendidos; los paréntesis dobles se usan para la aritmética; Las llaves {}se utilizan para la agrupación de comandos o multitudes de tipos de expansión de parámetros o expansión de llaves o expansión de secuencia. Estoy seguro de que he perdido algunos otros usos también ...
ephemient
44
El doble igual en la expresión if [ $VARIABLE == abcdef ]es un bashismo que, aunque funciona, probablemente debería evitarse; explícitamente use bash ( if [[ ...==...]]) o deje en claro que está usando el condicional más tradicional ( if [ "$VARIABLE" = "abcdef" ]). Podría decirse que los scripts deberían comenzar de la manera más simple y portátil posible, hasta que realmente necesiten características específicas de bash (por una razón u otra). Pero en cualquier caso, la intención debe ser clara; "=" y "==" y "[[" y "[" funcionan de manera diferente y su uso debe ser coherente.
michael
3
@michael_n: +1 para este comentario. En una nota al margen, me encantan las secuencias de comandos, pero me resulta bastante incómodo que la forma portátil sea probar a través de en [ "$var" = ".."]lugar de ==, mientras que en C asignaría en lugar de probar (y es una causa bastante común de errores) ... ¿por qué no 't testutilizar ==en lugar de =? ¿alguien sabe?
Olivier Dulac
También aquí hay algo curioso que (al menos en Kubuntu) el comando /usr/bin/[no es un enlace simbólico al /usr/bin/test, y más: ¡estos programas incluso tienen algunos tamaños diferentes!
Hola Ángel
Además: un paréntesis de cierre único )es parte de la casesintaxis de la declaración para finalizar una línea de caso. No tiene un paréntesis de apertura. Esto me sorprendió la primera vez que lo vi.
Agustín Amenabar
302

Soportes

if [ CONDITION ]    Test construct  
if [[ CONDITION ]]  Extended test construct  
Array[1]=element1   Array initialization  
[a-z]               Range of characters within a Regular Expression
$[ expression ]     A non-standard & obsolete version of $(( expression )) [1]

[1] http://wiki.bash-hackers.org/scripting/obsolete

Llaves

${variable}                             Parameter substitution  
${!variable}                            Indirect variable reference  
{ command1; command2; . . . commandN; } Block of code  
{string1,string2,string3,...}           Brace expansion  
{a..z}                                  Extended brace expansion  
{}                                      Text replacement, after find and xargs

Paréntesis

( command1; command2 )             Command group executed within a subshell  
Array=(element1 element2 element3) Array initialization  
result=$(COMMAND)                  Command substitution, new style  
>(COMMAND)                         Process substitution  
<(COMMAND)                         Process substitution 

Paréntesis dobles

(( var = 78 ))            Integer arithmetic   
var=$(( 20 + 5 ))         Integer arithmetic, with variable assignment   
(( var++ ))               C-style variable increment   
(( var-- ))               C-style variable decrement   
(( var0 = var1<98?9:21 )) C-style ternary operation
Yola
fuente
@Yola, ¿podría explicar qué hace $ (varname) exactamente? En los proyectos Apple Xcode, puedo especificar rutas de archivos como entrada / salida de script. si especifico $ SRC_ROOT / myFile.txt o $ {SRC_ROOT} /myFile.txt (SRC_ROOT var es exportado por el sistema de compilación) - no funciona. solo $ (SRC_ROOT) /myFile.txt funciona. ¿Cuál podría ser la razón? claramente var nombre no es un comando?
Motti Shneor
1
@MottiShneor, en su caso $(varname)no está relacionado con la sintaxis bash. Es parte de la sintaxis de Makefile .
Sasha
No es así: Xcode no se construye utilizando archivos MAKE y sus variables son variables de entorno. Los procesos patentados del sistema de compilación Xcode leen los valores de estas variables de entorno predefinidas. Los pasos de compilación personalizados son solo scripts de shell normales (bash u otros) y tienen acceso a los mismos vars.
Motti Shneor
@MottiShneor, ok, refinamos: lo más probable es que sea parte de la sintaxis de xcconfig . De todos modos, $(varname)no tiene relación con la sintaxis bash en su caso.
Sasha
No menciona cuál es la diferencia entre la construcción de prueba y la construcción de prueba extendida.
Nikos
23

Solo quería agregarlos desde TLDP :

~:$ echo $SHELL
/bin/bash

~:$ echo ${#SHELL}
9

~:$ ARRAY=(one two three)

~:$ echo ${#ARRAY}
3

~:$ echo ${TEST:-test}
test

~:$ echo $TEST


~:$ export TEST=a_string

~:$ echo ${TEST:-test}
a_string

~:$ echo ${TEST2:-$TEST}
a_string

~:$ echo $TEST2


~:$ echo ${TEST2:=$TEST}
a_string

~:$ echo $TEST2
a_string

~:$ export STRING="thisisaverylongname"

~:$ echo ${STRING:4}
isaverylongname

~:$ echo ${STRING:6:5}
avery

~:$ echo ${ARRAY[*]}
one two one three one four

~:$ echo ${ARRAY[*]#one}
two three four

~:$ echo ${ARRAY[*]#t}
one wo one hree one four

~:$ echo ${ARRAY[*]#t*}
one wo one hree one four

~:$ echo ${ARRAY[*]##t*}
one one one four

~:$ echo $STRING
thisisaverylongname

~:$ echo ${STRING%name}
thisisaverylong

~:$ echo ${STRING/name/string}
thisisaverylongstring
kzh
fuente
18
¡Cuidado que echo ${#ARRAY}muestra tres, porque el primer elemento ARRAYcontiene tres caracteres, no porque contiene tres elementos! Para imprimir el número de elementos use echo ${#ARRAY[@]}.
Verdaderamente el
@zeal ${TEST:-test}es igual a $TESTsi la variable TESTexiste, de lo contrario, simplemente devuelve la cadena "prueba". Hay otra versión que hace aún más: ${TEST:=test}--- que también equivale a $TESTsi TEST existe, pero siempre que no existe, crea la variable TESTy asigna un valor "prueba" y también se convierte en el valor de toda la expresión.
Ama Probabilidad
18

La diferencia entre test , [ y [[ se explica con gran detalle en BashFAQ .

Para abreviar una larga historia: la prueba implementa la sintaxis antigua y portátil del comando. En casi todos los shells (los shells Bourne más antiguos son la excepción), [es un sinónimo de prueba (pero requiere un argumento final de]). Aunque todos los shells modernos tienen implementaciones integradas de [, generalmente todavía hay un ejecutable externo con ese nombre, por ejemplo / bin / [.

[[es una nueva versión mejorada de la misma, que es una palabra clave, no un programa. Esto tiene efectos beneficiosos sobre la facilidad de uso, como se muestra a continuación. [[es entendido por KornShell y BASH (por ejemplo, 2.03), pero no por POSIX o BourneShell anteriores.

Y la conclusión:

¿Cuándo se debe usar el nuevo comando de prueba [[y cuándo se usa el anterior [? Si la portabilidad al BourneShell es una preocupación, se debe usar la sintaxis anterior. Si, por otro lado, el script requiere BASH o KornShell, la nueva sintaxis es mucho más flexible.

fwhacking
fuente
18

Paréntesis en la definición de funciones

Los paréntesis ()se utilizan en la definición de funciones:

function_name () { command1 ; command2 ; }

Esa es la razón por la que debe escapar del paréntesis incluso en los parámetros de comando:

$ echo (
bash: syntax error near unexpected token `newline'

$ echo \(
(

$ echo () { command echo The command echo was redefined. ; }
$ echo anything
The command echo was redefined.
pabouk
fuente
Oh, intenté con el csh. Culpa mía. Cuando pruebo en bash, funciona. No conocía el comando 'comando' de bash.
Chan Kim
¿Cómo puedo cancelar la redefinición del comando echo ()? (sin reabrir la fiesta)
Chan Kim
2
@ChanKim: unset -f echo. Ver help unset.
pabouk
0
Truncate the contents of a variable

$ var="abcde"; echo ${var%d*}
abc

Make substitutions similar to sed

$ var="abcde"; echo ${var/de/12}
abc12

Use a default value

$ default="hello"; unset var; echo ${var:-$default}
hello
vuppala srikar
fuente
Cuando responda una pregunta, no solo apunte a "código", sino que también intente agregar una explicación ...
Mikev