• Uncategorized

About bash : How-to-concatenate-string-variables-in-Bash

Question Detail

In PHP, strings are concatenated together as follows:

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

Here, $foo becomes "Hello World".

How is this accomplished in Bash?

Question Answer

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

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

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

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

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



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. 😉

echo $a

Using the Stack Overflow question syntax,

foo+=" World"
echo $foo
Hello World

works fine!

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

variable a is a string, but also an integer

echo $a
echo $a

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

Our a is also an array of only one element.

echo ${a[@]}


echo ${a[@]}
36 18
echo ${a[0]}
echo ${a[1]}

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:

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"
<hello world!>
<hello world!>
<hello world!>

Using the Stack Overflow question syntax:

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.


But you could simply do:

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=$(printf "%s World" "$foo")
echo $foo
Hello World

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

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

You can do this too:

$ var="myscript"

$ echo $var


$ var=${var}.sh

$ echo $var


echo "${bla}ohai${laber}bye"

Will output


This is useful when
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.

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


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

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:

  • BashFAQ/013 – How can I concatenate two variables?
  • When do we need curly braces around shell variables?

The way I’d solve the problem is just


For example,

b=" World"
echo "$c"

which produces

Hello World

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

c="$a World"

then echo "$c" will produce

Hello World

with an extra space.


doesn’t work, as you may imagine, but




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

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.

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


This does not work:


This works fine:


The simplest way with quotation marks:

echo "Hello ""$var"

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="$foo World"
echo $foo
> Hello World

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:

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

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

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

Curly brackets will fit to Continuous string usage:

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=$foo' World'
echo $foo

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

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.

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

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

Safer way:

d="DD DD"
echo "$s"

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

Plus take a look at other answer about +=

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

cat > output.file << EOF

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

cat > output.file << EOF

a=$a" World!"
echo $a

This is how you concatenate two strings.

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


foo=$foo" World"
echo $foo

The output:

Hello World

var3=$var1" "$var2 
echo $var3

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

    ctime=$(date +%s.%N);
    delta=$(bc <<<"$ctime - $ptime");
    printf "%2s. %16s chars  time: %s  delta: %s\n" $n "$(bc <<<"10*(2^$n)")" $ctime $delta;

    echo 'Method: a="$a$a"'
    for n in {1..32}; do a="$a$a"; output; done

    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.

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)

for elm in "${list[@]}"; do
    string="${string} ${elm}"

echo ${string}

and then I get the following output:

1 2 3 4 5

Note that this won’t work


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


but this will work:


and leave you with the correct output:


Here is the one through AWK:

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

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

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

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 ()

    echo "${prefix}${suffix}"


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 [email protected]

echo $(combo 'foo''bar')

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

You may also like...

Leave a Reply

Your email address will not be published.

This site uses Akismet to reduce spam. Learn how your comment data is processed.