Cómo concatenar variables de cadena en Bash

En PHP, las cadenas se concatenan juntas de la siguiente manera:

$foo = "Hello"; $foo .= " World"; 

Aquí, $foo convierte en “Hello World”.

¿Cómo se logra esto en Bash?

 foo="Hello" foo="$foo World" echo $foo > Hello World 

En general, para concatenar dos variables puedes escribirlas una tras otra:

 a='hello' b='world' c=$a$b echo $c > helloworld 

Bash también admite un operador + = como se muestra en la siguiente transcripción:

 $ A="XY" $ A+="Z" $ echo "$A" X YZ 

Bash primero

Como esta pregunta es específica para Bash , mi primera parte de la respuesta presentaría diferentes formas de hacerlo correctamente:

+= : Agregar a la variable

La syntax += puede usarse de diferentes maneras:

Agregar a la cadena var+=...

(Debido a que soy frugal, solo usaré dos variables foo y a y luego volveré a usar el mismo en toda la respuesta; 😉

 a=2 a+=4 echo $a 24 

Usando la syntax de la pregunta de desbordamiento de stack ,

 foo="Hello" foo+=" World" echo $foo Hello World 

¡funciona bien!

Agregar a un entero ((var+=...))

la variable a es una cadena, pero también un número entero

 echo $a 24 ((a+=12)) echo $a 36 

Agregar a una matriz var+=(...)

Nuestra a también es una matriz de un solo elemento.

 echo ${a[@]} 36 a+=(18) echo ${a[@]} 36 18 echo ${a[0]} 36 echo ${a[1]} 18 

Tenga en cuenta que entre paréntesis, hay una matriz separada por espacios . Si desea almacenar una cadena que contenga espacios en su matriz, debe adjuntarlos:

 a+=(one word "hello world!" ) bash: !": event not found 

Hmm … esto no es un error, sino una característica … ¡Para evitar que bash intente desarrollarse !" , Podrías:

 a+=(one word "hello world"! 'hello world!' $'hello world\041') declare -pa declare -aa='([0]="36" [1]="18" [2]="one" [3]="word" [4]="hello world!" [5]="h ello world!" [6]="hello world!")' 

printf : reconstruye la variable usando el comando incorporado

El comando printf builtin brinda una forma poderosa de dibujar el formato de cadena. Como se trata de un Bash incorporado , hay una opción para enviar cadenas formateadas a una variable en lugar de imprimir en stdout :

 echo ${a[@]} 36 18 one word hello world! hello world! hello world! 

Hay siete cadenas en este conjunto. Entonces podríamos construir una cadena formateada que contenga exactamente siete argumentos posicionales:

 printf -va "%s./.%s...'%s' '%s', '%s'=='%s'=='%s'" "${a[@]}" echo $a 36./.18...'one' 'word', 'hello world!'=='hello world!'=='hello world!' 

O podríamos usar una cadena de formato de argumento que se repetirá como muchos argumentos enviados …

Tenga en cuenta que nuestra a sigue siendo una matriz! ¡Solo se cambia el primer elemento!

 declare -pa declare -aa='([0]="36./.18...'\''one'\'' '\''word'\'', '\''hello world!'\''=='\ ''hello world!'\''=='\''hello world!'\''" [1]="18" [2]="one" [3]="word" [4]="hel lo world!" [5]="hello world!" [6]="hello world!")' 

Bajo bash, cuando accede a un nombre de variable sin especificar el índice, ¡siempre se dirige solo al primer elemento!

Entonces, para recuperar nuestra matriz de siete campos, solo tenemos que volver a establecer el primer elemento:

 a=36 declare -pa declare -aa='([0]="36" [1]="18" [2]="one" [3]="word" [4]="hello world!" [5]="he llo world!" [6]="hello world!")' 

Una cadena de formato de argumento con muchos argumentos pasados ​​a:

 printf -va[0] '<%s>\n' "${a[@]}" echo "$a" <36> <18>      

Usando la syntax de la pregunta sobre desbordamiento de stack :

 foo="Hello" printf -v foo "%s World" $foo echo $foo Hello World 

Nota: el uso de comillas dobles puede ser útil para manipular cadenas que contienen spaces , tabulations y / o newlines

 printf -v foo "%s World" "$foo" 

Shell ahora

Bajo el shell POSIX , no se pueden usar bashisms , por lo que no hay printf incorporado .

Básicamente

Pero podrías simplemente hacer:

 foo="Hello" foo="$foo World" echo $foo Hello World 

Formateado, utilizando printf bifurcado

Si desea utilizar construcciones más sofisticadas, debe usar un tenedor (proceso hijo nuevo que hace el trabajo y devolver el resultado mediante stdout ):

 foo="Hello" foo=$(printf "%s World" "$foo") echo $foo Hello World 

Históricamente, puedes usar los trazos inversos para recuperar el resultado de un tenedor :

 foo="Hello" foo=`printf "%s World" "$foo"` echo $foo Hello World 

Pero esto no es fácil para anidar :

 foo="Today is: " foo=$(printf "%s %s" "$foo" "$(date)") echo $foo Today is: Sun Aug 4 11:58:23 CEST 2013 

con los apoyos hacia atrás, debes escapar de los tenedores interiores con barras invertidas :

 foo="Today is: " foo=`printf "%s %s" "$foo" "\`date\`"` echo $foo Today is: Sun Aug 4 11:59:10 CEST 2013 

Usted puede hacer esto también:

 $ var="myscript" $ echo $var myscript $ var=${var}.sh $ echo $var myscript.sh 
 bla=hello laber=kthx echo "${bla}ohai${laber}bye" 

Producirá

 helloohaikthxbye 

Esto es útil cuando $blaohai lleva a un error variable no encontrado. O si tiene espacios u otros caracteres especiales en sus cadenas. "${foo}" escapa correctamente todo lo que le pongas.

 foo="Hello " foo="$foo World" 

La forma en que resolvería el problema es solo

 $a$b 

Por ejemplo,

 a="Hello" b=" World" c=$a$b echo "$c" 

que produce

 Hello World 

Si intenta concatenar una cadena con otra cadena, por ejemplo,

 a="Hello" c="$a World" 

entonces echo "$c" producirá

 Hello World 

con un espacio extra

 $aWorld 

no funciona, como te puedes imaginar, pero

 ${a}World 

produce

 HelloWorld 
 $ a=hip $ b=hop $ ab=$a$b $ echo $ab hiphop $ echo $a$b hiphop 

Sin embargo, otro enfoque …

 > H="Hello " > U="$H""universe." > echo $U Hello universe. 

… y aún otro más.

 > H="Hello " > U=$H"universe." > echo $U Hello universe. 

Si desea agregar algo como un guión bajo, use escape (\)

 FILEPATH=/opt/myfile 

Esto no funciona:

 echo $FILEPATH_$DATEX 

Esto funciona bien:

 echo $FILEPATH\\_$DATEX 

Aquí hay un resumen conciso de lo que la mayoría de las respuestas están hablando.

Digamos que tenemos dos variables:

 a=hello b=world 

La siguiente tabla explica los diferentes contextos en los que podemos combinar los valores de a y b para crear una nueva variable, c .

 Context | Expression | Result (value of c) --------------------------------------+-----------------------+--------------------- Two variables | c=$a$b | helloworld A variable and a literal | c=${a}_world | hello_world A variable, a literal, with a space | c=${a}" world" | hello world A more complex expression | c="${a}_one|${b}_2" | hello_one|world_2 Using += operator (Bash 3.1 or later) | c=$a; c+=$b | helloworld Append literal with += | c=$a; c+=" world" | hello world 

Algunas notas:

  • adjuntar el RHS de una tarea entre comillas dobles es generalmente una buena práctica, aunque es bastante opcional en muchos casos
  • += es mejor desde el punto de vista del rendimiento si se está construyendo una gran cadena en pequeños incrementos, especialmente en un ciclo
  • use {} alrededor de nombres de variables para eliminar la ambigüedad de su expansión (como en la fila 2 en la tabla anterior)

Ver también:

  • BashFAQ / 013 – ¿Cómo puedo concatenar dos variables?
  • ¿Cuándo necesitamos aparatos ortopédicos alrededor de las variables de la carcasa?

Puede concatenar sin las comillas. Aquí hay un ejemplo:

 $Variable1 Open $Variable2 Systems $Variable3 $Variable1$Variable2 $echo $Variable3 

Esta última statement imprimiría “OpenSystems” (sin comillas).

Este es un ejemplo de un script Bash:

 v1=hello v2=world v3="$v1 $v2" echo $v3 # Output: hello world echo "$v3" # Output: hello world 

La forma más simple con comillas:

 B=Bar b=bar var="$B""$b""a" echo "Hello ""$var" 

Incluso si el operador + = ahora está permitido, se introdujo en Bash 3.1 en 2004.

Cualquier secuencia de comandos que use este operador en las versiones anteriores de Bash fallará con un error de “comando no encontrado” si tiene suerte, o un “error de syntax cerca del token inesperado”.

Para aquellos que se preocupan por la compatibilidad con versiones anteriores, se adhieren a los métodos estándar de concatenación de Bash, como los mencionados en la respuesta elegida:

 foo="Hello" foo="$foo World" echo $foo > Hello World 

Prefiero usar corchetes ${} para expandir variable en cadena:

 foo="Hello" foo="${foo} World" echo $foo > Hello World 

Los corchetes se ajustarán al uso continuo de cadenas:

 foo="Hello" foo="${foo}World" echo $foo > HelloWorld 

De lo contrario, usar foo = "$fooWorld" no funcionará.

Manera más segura:

 a="AAAAAAAAAAAA" b="BBBBBBBBBBBB" c="CCCCCCCCCCCC" d="DD DD" s="${a}${b}${c}${d}" echo "$s" AAAAAAAAAAAABBBBBBBBBBBBCCCCCCCCCCCCDD DD 

Las cadenas que contienen espacios pueden formar parte del comando, use “$ XXX” y “$ {XXX}” para evitar estos errores.

Además, eche un vistazo a otra respuesta sobre + =

Hay un caso particular en el que debes tener cuidado:

 user=daniel cat > output.file << EOF "$user"san EOF 

danielsan salida a "daniel"san , y no a danielsan , como hubieras querido. En este caso, debe hacer en su lugar:

 user=daniel cat > output.file << EOF ${user}san EOF 

Si lo que intenta hacer es dividir una cadena en varias líneas, puede usar una barra diagonal inversa:

 $ a="hello\ > world" $ echo $a helloworld 

Con un espacio intermedio:

 $ a="hello \ > world" $ echo $a hello world 

Este también agrega solo un espacio en el medio:

 $ a="hello \ > world" $ echo $a hello world 
 var1='hello' var2='world' var3=$var1" "$var2 echo $var3 

Si es como su ejemplo de agregar " World" a la cadena original, entonces puede ser:

 #!/bin/bash foo="Hello" foo=$foo" World" echo $foo 

La salida:

 Hello World 

Tenga en cuenta que esto no funcionará

 foo=HELLO bar=WORLD foobar=PREFIX_$foo_$bar 

ya que parece dejar $ foo y te deja con:

PREFIX_WORLD

pero esto funcionará

 foobar=PREFIX_"$foo"_"$bar" 

y te dejo el resultado correcto:

PREFIX_HELLO_WORLD

Hay inquietudes expresadas sobre el rendimiento, pero no se ofrecen datos. Déjame sugerirte una prueba simple.

(NOTA: la date en macOS no ofrece nanosegundos, por lo que esto debe hacerse en Linux.)

He creado append_test.sh en GitHub con los contenidos:

 #!/bin/bash -e output(){ ptime=$ctime; ctime=$(date +%s.%N); delta=$(bc <<<"$ctime - $ptime"); printf "%2s. %16s chars time: %s delta: %s\n" $n "$(bc <<<"10*(2^$n)")" $ctime $delta; } method1(){ echo 'Method: a="$a$a"' for n in {1..32}; do a="$a$a"; output; done } method2(){ echo 'Method: a+="$a"' for n in {1..32}; do a+="$a"; output; done } ctime=0; a="0123456789"; time method$1 

Prueba 1:

 $ ./append_test.sh 1 Method: a="$a$a" 1. 20 chars time: 1513640431.861671143 delta: 1513640431.861671143 2. 40 chars time: 1513640431.865036344 delta: .003365201 3. 80 chars time: 1513640431.868200952 delta: .003164608 4. 160 chars time: 1513640431.871273553 delta: .003072601 5. 320 chars time: 1513640431.874358253 delta: .003084700 6. 640 chars time: 1513640431.877454625 delta: .003096372 7. 1280 chars time: 1513640431.880551786 delta: .003097161 8. 2560 chars time: 1513640431.883652169 delta: .003100383 9. 5120 chars time: 1513640431.886777451 delta: .003125282 10. 10240 chars time: 1513640431.890066444 delta: .003288993 11. 20480 chars time: 1513640431.893488326 delta: .003421882 12. 40960 chars time: 1513640431.897273327 delta: .003785001 13. 81920 chars time: 1513640431.901740563 delta: .004467236 14. 163840 chars time: 1513640431.907592388 delta: .005851825 15. 327680 chars time: 1513640431.916233664 delta: .008641276 16. 655360 chars time: 1513640431.930577599 delta: .014343935 17. 1310720 chars time: 1513640431.954343112 delta: .023765513 18. 2621440 chars time: 1513640431.999438581 delta: .045095469 19. 5242880 chars time: 1513640432.086792464 delta: .087353883 20. 10485760 chars time: 1513640432.278492932 delta: .191700468 21. 20971520 chars time: 1513640432.672274631 delta: .393781699 22. 41943040 chars time: 1513640433.456406517 delta: .784131886 23. 83886080 chars time: 1513640435.012385162 delta: 1.555978645 24. 167772160 chars time: 1513640438.103865613 delta: 3.091480451 25. 335544320 chars time: 1513640444.267009677 delta: 6.163144064 ./append_test.sh: fork: Cannot allocate memory 

Prueba 2:

 $ ./append_test.sh 2 Method: a+="$a" 1. 20 chars time: 1513640473.460480052 delta: 1513640473.460480052 2. 40 chars time: 1513640473.463738638 delta: .003258586 3. 80 chars time: 1513640473.466868613 delta: .003129975 4. 160 chars time: 1513640473.469948300 delta: .003079687 5. 320 chars time: 1513640473.473001255 delta: .003052955 6. 640 chars time: 1513640473.476086165 delta: .003084910 7. 1280 chars time: 1513640473.479196664 delta: .003110499 8. 2560 chars time: 1513640473.482355769 delta: .003159105 9. 5120 chars time: 1513640473.485495401 delta: .003139632 10. 10240 chars time: 1513640473.488655040 delta: .003159639 11. 20480 chars time: 1513640473.491946159 delta: .003291119 12. 40960 chars time: 1513640473.495354094 delta: .003407935 13. 81920 chars time: 1513640473.499138230 delta: .003784136 14. 163840 chars time: 1513640473.503646917 delta: .004508687 15. 327680 chars time: 1513640473.509647651 delta: .006000734 16. 655360 chars time: 1513640473.518517787 delta: .008870136 17. 1310720 chars time: 1513640473.533228130 delta: .014710343 18. 2621440 chars time: 1513640473.560111613 delta: .026883483 19. 5242880 chars time: 1513640473.606959569 delta: .046847956 20. 10485760 chars time: 1513640473.699051712 delta: .092092143 21. 20971520 chars time: 1513640473.898097661 delta: .199045949 22. 41943040 chars time: 1513640474.299620758 delta: .401523097 23. 83886080 chars time: 1513640475.092311556 delta: .792690798 24. 167772160 chars time: 1513640476.660698221 delta: 1.568386665 25. 335544320 chars time: 1513640479.776806227 delta: 3.116108006 ./append_test.sh: fork: Cannot allocate memory 

Los errores indican que mi Bash obtuvo hasta 335.54432 MB antes de que se bloquee . Podría cambiar el código de duplicar los datos a agregar una constante para obtener un gráfico más granular y un punto de falla. Pero creo que esto debería darte suficiente información para decidir si te importa. Personalmente, debajo de 100 MB, no. Su experiencia puede ser diferente.

Lo hago de esta manera cuando sea conveniente: ¡Use un comando en línea!

 echo "The current time is `date`" echo "Current User: `echo $USER`" 

Aquí está el de AWK :

 $ foo="Hello" $ foo=$(awk -v var=$foo 'BEGIN{print var" World"}') $ echo $foo Hello World 

Me gusta hacer una función rápida.

 #! /bin/sh -f function combo() { echo $@ } echo $(combo 'foo''bar') 

Otra forma de despellejar a un gato. Esta vez con funciones: D

Todavía no sé nada de PHP, pero esto funciona en Linux Bash. Si no desea afectarlo a una variable, puede intentar esto:

 read pp; *# Assumes I will affect Hello to pp* pp=$( printf $pp ;printf ' World'; printf '!'); echo $pp; >Hello World! 

Puede colocar otra variable en lugar de ‘Hola’ o ‘!’. También podría concatenar más cadenas.

Puede intentarlo a continuación. Cuando ocurre la sustitución, las comillas dobles mantendrán los espacios.

 var1="Ram " var2="Lakshmana" echo $var1$var2 or echo var1+=$var2 "bash support += operation. bcsmc2rtese001 [/tmp]$ var1="Ram " bcsmc2rtese001 [/tmp]$ var2="Lakshmana" bcsmc2rtese001 [/tmp]$ echo $var1$var2 Ram Lakshmana bcsmc2rtese001 [/tmp]$ var1+=$var2 bcsmc2rtese001 [/tmp]$ echo $var1 Ram Lakshmana