How to concatenate string variables in Bash

In PHP, strings are concatenated together as follows:

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

Here, $foo becomes "Hello World".

How is this accomplished in Bash?

5

30 Answers

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

In general to concatenate two variables you can just write them one after another:

a='Hello' b='World' c="${a} ${b}" echo "${c}" > Hello World 
15

Bash also supports a += operator as shown in this code:

A="X Y" A+=" Z" echo "$A" 

output

X Y Z

10

Bash first

As this question stand specifically for Bash, my first part of the answer would present different ways of doing this properly:

+=: Append to variable

The syntax += may be used in different ways:

Append to string var+=...

(Because I am frugal, I will only use two variables foo and a and then re-use the same in the whole answer. ;-)

a=2 a+=4 echo $a 24 

Using the Stack Overflow question syntax,

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

works fine!

Append to an integer ((var+=...))

variable a is a string, but also an integer

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

Append to an array var+=(...)

Our a is also an array of only one element.

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

Note that between parentheses, there is a space separated array. If you want to store a string containing spaces in your array, you have to enclose them:

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

Hmm.. this is not a bug, but a feature... To prevent bash to try to develop !", you could:

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

printf: Re-construct variable using the builtin command

The printf builtin command gives a powerful way of drawing string format. As this is a Bash builtin, there is a option for sending formatted string to a variable instead of printing on stdout:

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

There are seven strings in this array. So we could build a formatted string containing exactly seven positional arguments:

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

Or we could use one argument format string which will be repeated as many argument submitted...

Note that our a is still an array! Only first element is changed!

declare -p a declare -a a='([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!")' 

Under bash, when you access a variable name without specifying index, you always address first element only!

So to retrieve our seven field array, we only need to re-set 1st element:

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

One argument format string with many argument passed to:

printf -v a[0] '<%s>\n' "${a[@]}" echo "$a" <36> <18> <one> <word> <hello world!> <hello world!> <hello world!> 

Using the Stack Overflow question syntax:

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

Nota: The use of double-quotes may be useful for manipulating strings that contain spaces, tabulations and/or newlines

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

Shell now

Under POSIX shell, you could not use bashisms, so there is no builtin printf.

Basically

But you could simply do:

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

Formatted, using forked printf

If you want to use more sophisticated constructions you have to use a fork (new child process that make the job and return the result via stdout):

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

Historically, you could use backticks for retrieving result of a fork:

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

But this is not easy for nesting:

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

with backticks, you have to escape inner forks with backslashes:

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

You can do this too:

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

Will output

helloohaikthxbye 

This is useful when $blaohai leads to a variable not found error. Or if you have spaces or other special characters in your strings. "${foo}" properly escapes anything you put into it.

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

     

3

Here is a concise summary of what most answers are talking about.

Let's say we have two variables and $1 is set to 'one':

set one two a=hello b=world 

The table below explains the different contexts where we can combine the values of a and b to create a new 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 and a literal | c=$1world | oneworld 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 

A few notes:

  • enclosing the RHS of an assignment in double quotes is generally a good practice, though it is quite optional in many cases
  • += is better from a performance standpoint if a big string is being constructed in small increments, especially in a loop
  • use {} around variable names to disambiguate their expansion (as in row 2 in the table above). As seen on rows 3 and 4, there is no need for {} unless a variable is being concatenated with a string that starts with a character that is a valid first character in shell variable name, that is alphabet or underscore.

See also:

1

The way I'd solve the problem is just

$a$b 

For example,

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

which produces

Hello World 

If you try to concatenate a string with another string, for example,

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

then echo "$c" will produce

Hello World 

with an extra space.

$aWorld 

doesn't work, as you may imagine, but

${a}World 

produces

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

Yet another approach...

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

...and yet yet another one.

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

If you want to append something like an underscore, use escape (\)

FILEPATH=/opt/myfile 

This does not work:

echo $FILEPATH_$DATEX 

This works fine:

echo $FILEPATH\\_$DATEX 
3

The simplest way with quotation marks:

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

Even if the += operator is now permitted, it has been introduced in Bash 3.1 in 2004.

Any script using this operator on older Bash versions will fail with a "command not found" error if you are lucky, or a "syntax error near unexpected token".

For those who cares about backward compatibility, stick with the older standard Bash concatenation methods, like those mentioned in the chosen answer:

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

You can concatenate without the quotes. Here is an example:

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

This last statement would print "OpenSystems" (without quotes).

This is an example of a Bash script:

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

I prefer to use curly brackets ${} for expanding variable in string:

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

Curly brackets will fit to Continuous string usage:

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

Otherwise using foo = "$fooWorld" will not work.

Despite of the special operator, +=, for concatenation, there is a simpler way to go:

foo='Hello' foo=$foo' World' echo $foo 

Double quotes take an extra calculation time for interpretation of variables inside. Avoid it if possible.

If what you are trying to do is to split a string into several lines, you can use a backslash:

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

With one space in between:

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

This one also adds only one space in between:

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

Safer way:

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

Strings containing spaces can become part of command, use "$XXX" and "${XXX}" to avoid these errors.

Plus take a look at other answer about +=

1

There's one particular case where you should take care:

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

Will output "daniel"san, and not danielsan, as you might have wanted. In this case you should do instead:

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

Variables and arrays (indexed or associative*) in bash are always strings by default, but you can use flags to the declare builtin, to give them attributes like "integer" (-i) or "reference"** (-n), which change the way they behave.

Bash arithmetic accepts ASCII/string numbers for input, so there are few reasons to actually use the integer attribute.

Also, variable values can't contain ASCII NULL (ie. 8 bit zero), because regular null terminated C strings are used to implement them.

* Ie one or more key + value pairs.
** Reference variables expand to the value of another variable, whose label is assigned to the reference variable

Append a string:

$ foo=Hello $ foo+=' world!' $ echo "$foo" Hello world! $ num=3 $ num+=4 echo "$num" 34 # Appended string (not a sum) 

One of the few reasons to use the integer attribute, is that it changes the behaviour of the += assignment operator:

$ declare -i num=3 $ num+=4 echo "$num" 7 # Sum 

Note that this doesn't work for -=, /=, etc. unless you do it inside arithmetic ((( )) and $(( ))), where numbers are already treated the same with or without the integer attribute. See the section "arithmetic evaluation" of man bash for a full list of those operators, which are the same as for C.

The += assignment operator can also be used to append new elements to an indexed array (AKA "list"):

$ foo=(one) $ foo+=(two) $ printf 'Separate element: %s\n' "${foo[@]}" Separate element: one Separate element: two 

Another common way to do this is to use a counter:

$ foo[c++]=one $ foo[c++]=two 

POSIX shells do not use the += assignment operator to append strings, so you have to do it like this:

$ foo=Hello $ foo="$foo world!" $ echo "$foo" Hello world! 

This is fine in bash too, so it could be considered a more portable syntax.

a="Hello," a=$a" World!" echo $a 

This is how you concatenate two strings.

1

If it is as your example of adding " World" to the original string, then it can be:

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

The output:

Hello World 
var1='hello' var2='world' var3=$var1" "$var2 echo $var3 
1

There are voiced concerns about performance, but no data is offered. Let me suggest a simple test.

(NOTE: date on macOS does not offer nanoseconds, so this must be done on Linux.)

I have created append_test.sh on GitHub with the contents:

#!/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 

Test 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 

Test 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 

The errors indicate that my Bash got up to 335.54432 MB before it crashed. You could change the code from doubling the data to appending a constant to get a more granular graph and failure point. But I think this should give you enough information to decide whether you care. Personally, below 100 MB I don't. Your mileage may vary.

1

I wanted to build a string from a list. Couldn't find an answer for that so I post it here. Here is what I did:

list=(1 2 3 4 5) string='' for elm in "${list[@]}"; do string="${string} ${elm}" done echo ${string} 

and then I get the following output:

1 2 3 4 5 

Note that this won't work

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

as it seems to drop $foo and leaves you with:

PREFIX_WORLD

but this will work:

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

and leave you with the correct output:

PREFIX_HELLO_WORLD

1

Here is the one through AWK:

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

I do it this way when convenient: Use an inline command!

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

In my opinion, the simplest way to concatenate two strings is to write a function that does it for you, then use that function.

function concat () { prefix=$1 suffix=$2 echo "${prefix}${suffix}" } foo="Super" bar="man" concat $foo $bar # Superman alien=$(concat $foo $bar) echo $alien # Superman 

I kind of like making a quick function.

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

Yet another way to skin a cat. This time with functions :D

1

You Might Also Like