How to concatenate string variables in Bash

4,451,315

Solution 1

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

Solution 2

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

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

output

X Y Z

Solution 3

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

Solution 4

You can do this too:

$ var="myscript"

$ echo $var

myscript


$ var=${var}.sh

$ echo $var

myscript.sh

Solution 5

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.

Share:
4,451,315
Strawberry
Author by

Strawberry

I want to learn industry practices and apply them to my projects to make myself successful

Updated on July 08, 2022

Comments

  • Strawberry
    Strawberry almost 2 years

    In PHP, strings are concatenated together as follows:

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

    Here, $foo becomes "Hello World".

    How is this accomplished in Bash?

    • parasrish
      parasrish about 6 years
      foo="Hello" foo=$foo" World" echo $foo this rather worked for "#!/bin/sh"
    • Adi
      Adi almost 6 years
      What to do if you want HelloWorld without space?
    • GeneCode
      GeneCode about 5 years
      @Adi foo1="World" foo2="Hello" foo3="$foo1$foo2"
    • Capibar
      Capibar almost 5 years
      spaces does a matter in bash)
    • Charlie Parker
      Charlie Parker almost 3 years
      to give an example of inserting into a string do echo "sh ${HOME}/ultimate-utils/run_tb.sh"
  • installero
    installero over 11 years
    I'm afraid this is not what was meant
  • Stefan
    Stefan about 11 years
    This is the most useful answer for shell scripting. I have found myself the last 30 minutes because I had a space before and after the equal sign!!
  • user3383301
    user3383301 almost 11 years
    Doesn't work. I get "backupstorefolder: command not found" from bash where "backupstorefolder" is the name of a variable.
  • nonsensickle
    nonsensickle over 10 years
    Does there have to be a space in your first example? Is it possible to do something like foo="$fooworld"? I would assume not...
  • Nik O'Lai
    Nik O'Lai over 10 years
    this happens because the underscore is the valid character in variable names, so bash sees foo_ as a variable. When it is necessary to tell bash the exact var name boundaries, the curly braces can be used: PREFIX_${foo}_$bar
  • Nik O'Lai
    Nik O'Lai over 10 years
    Or alternatively, ${FILEPATH}_$DATEX. Here {} are used to indicate the boundaries of the variable name. This is appropariate because the underscore is a legal character in variable names, so in your snippet bash actually tries to resolve FILEPATH_, not just $FILEPATH
  • twalberg
    twalberg about 10 years
    @nonsensickle That would look for a variable named fooworld. Disambiguating that is done with braces, as in foo="${foo}world"...
  • levesque
    levesque about 10 years
    Can I use this syntax with the export keyword? e.g. export A+="Z" or maybe the A variable only needs to be exported once?
  • thkala
    thkala about 10 years
    @levesque: Both :-). Variables only need to be exported once, but export A+=Z works quite nicely as well.
  • F. Hauri  - Give Up GitHub
    F. Hauri - Give Up GitHub almost 10 years
    While no special chars, nor spaces are used, double quotes, quotes and curly brackets are useless: var=myscript;var=$var.sh;echo $var would have same effects (This work under bash, dash, busybox and others).
  • F. Hauri  - Give Up GitHub
    F. Hauri - Give Up GitHub almost 10 years
    On 1st line, you could drop a fork by using: date "+The current time is %a %b %d %Y +%T", instead of echo ...$(date). Under recent bash, you could write: printf "The current time is %(%a %b %d %Y +%T)T\n" -1 .
  • techno
    techno over 9 years
    Nice, But I think I could obtain more precision by using Python !
  • JVE999
    JVE999 over 9 years
    @twalberg I've found you can also use foo=$foo'world'
  • twalberg
    twalberg over 9 years
    @JVE999 Yes, that works as well, although in my opinion it's not quite as good for code clarity... But that may just be my preference... There are a couple other ways it can be done as well - the point is making sure the variable name is separated from the non-variable-name parts so that it parses correctly.
  • Mason Heller
    Mason Heller over 9 years
    One problem with putting the whole thing in double quotes, as with foo="$foo World", is that the additional string ("world" in this case) would be interpreted by the shell if it contained variable names, etc, which is usually not wanted. IMO, the common case requires the syntax $foo="$foo"' world'.
  • petersohn
    petersohn about 9 years
    The problem with this solution is that it is very inefficient. the value of $foo is copied then the new value is appended, which takes a long time if the string is very long. It becomes especially apparent if you append strings in a loop, then the complexity of the loop becomes quadratic instead of linear.
  • Score_Under
    Score_Under about 9 years
    Since this is a bashism, I think it's worth a mention that you should never use #!/bin/sh in a script using this construction.
  • Rho Phi
    Rho Phi almost 9 years
    Thanks for pointing this out, I was just searching for which version is require for this to work.
  • M4tho
    M4tho over 8 years
    If i use this code it gives me " world" as output. what can i do to fix this?
  • Ray Foss
    Ray Foss over 8 years
    This helps syntax highlighting quite a bit, and removes some human ambiguity.
  • quimnuss
    quimnuss over 8 years
    That's what I did, and I found it so much simple and straight-forward than the other answers. Is there a reason nobody on the most voted answers pointed this option out?
  • Matt
    Matt about 8 years
    The += operator is also much faster than $a="$a$b" in my tests.. Which makes sense.
  • Indrajeet Gour
    Indrajeet Gour almost 8 years
    for me I had one variable i.e. $var1 and constant next to this, so echo $var1_costant_traling_part works for me
  • geneorama
    geneorama almost 8 years
    This answer is awesome, but I think it's missing the var=${var}.sh example from other answers, which is very useful.
  • dashesy
    dashesy almost 8 years
    Is bash the only shell with += operator? I want to see if it is portable enough
  • F. Hauri  - Give Up GitHub
    F. Hauri - Give Up GitHub almost 8 years
    @dashesy no. i'ts surely not the only shell with += operator, but all this ways are bashisms, so not portable! Even you could encounter special bug in case of wrong bash version!
  • XXL
    XXL over 7 years
    foo="${foo}World"
  • jlliagre
    jlliagre over 7 years
    @quimnuss The fact the strings do not match those used in the OP question might be a good reason.
  • Chaminda Bandara
    Chaminda Bandara over 7 years
    Can't print as follows in a single write ? echo 'User Name: $USER'
  • Fahad Naeem
    Fahad Naeem over 7 years
    In addition to the answer: str="World" echo "Hello "$str. If you want to concatenate a constant string and a variable.
  • XavierStuvw
    XavierStuvw over 7 years
    Also var3=$var1\ $var2 has the same effect
  • XavierStuvw
    XavierStuvw over 7 years
    Too many quotation marks, IMHO. var=$B$b"a"; echo Hello\ $var would do, I believe
  • XavierStuvw
    XavierStuvw over 7 years
    The point of the strings with a space that are read as a command shows up at the point of definition. So d=DD DD would give DD: command not found --- note that is the last DD, rather d that is not found. If all operands are properly formatted and already contain the required spaces, you can simply concatenate with s=${a}${b}${c}${d}; echo $s, with less quote marks. Also you could use \ (escaped whitespace) to avoid these issues --- d=echo\ echo will not launch any echo invocation, whereas d=echo echo will.
  • XavierStuvw
    XavierStuvw over 7 years
    The syntax of the first block is confusing. What do these $ signs mean?
  • XavierStuvw
    XavierStuvw over 7 years
    I guess one needs only one backlash for escaping: echo $a\_$b would do. As hinted in the comment of Nik O'Lai the underscore is a regular character. The handling of white spaces is much more sensitive for strings, echo and concatenation --- one can use \ and read this thread thoroughly as this issue comes back now and then.
  • XavierStuvw
    XavierStuvw over 7 years
    ...hence ${a}\ World produces Hello World
  • phpguru
    phpguru over 7 years
    It's specifically and only a plus-equals operator. That is, unlike Javascript, in Bash, echo $A+$B prints "X Y+Z"
  • codeaviator
    codeaviator over 6 years
    @Score_Under What is a bashism? Thanks
  • Score_Under
    Score_Under over 6 years
    A bashism is a shell feature which is only supported in bash and certain other more advanced shells. It will not work under busybox sh or dash (which is /bin/sh on a lot of distros), or certain other shells like the /bin/sh provided on FreeBSD.
  • JrBenito
    JrBenito over 6 years
    foo="$foo World" this can be extremely slow if the made in a loop with large strings (string growing every iteration). In this case, += very very faster
  • Bruno Bronosky
    Bruno Bronosky over 6 years
    If you are concerned about performance, see an analysis in my answer stackoverflow.com/a/47878161/117471
  • Bruno Bronosky
    Bruno Bronosky over 6 years
    If you are concerned about performance, see an analysis in my answer stackoverflow.com/a/47878161/117471
  • Danijel
    Danijel over 6 years
    Note: there are no spaces around =! This will not work: a ='hello', neither will this: a = ' hello', etc.
  • Doktor J
    Doktor J about 6 years
    @ChamindaBandara variables are not evaluated in single-quote strings. Use echo "User Name: $USER" instead (or better, echo "User Name: ${USER}"). If you absolutely, positively must use single quotes, put the variable outside the single quotes, i.e. echo 'User Name: '$USER.
  • betontalpfa
    betontalpfa almost 6 years
    I suggest to use all of the quotation marks, cause if you put it everywhere you cannot miss, you dont have to think.
  • codeforester
    codeforester over 5 years
    Calling a function, that too in a subshell, is too much an overkill for a simple concat.
  • mwfearnley
    mwfearnley over 5 years
    This surprises me; I would have expected c=$a$b here to do the same thing as c=$a World (which would try to run World as a command). I guess that means the assignment is parsed before the variables are expanded..
  • Anthony Rutledge
    Anthony Rutledge almost 5 years
    This works, but it will sometimes yield unpredictable results because the variable interpolation is not protected. Thus, you cannot rely on this form for all use cases.
  • Anthony Rutledge
    Anthony Rutledge almost 5 years
    Are there any use cases where this might yield an unpredictable result? Would it be better to create a generic concat function, instead of hoping that the += form does not hit an edge case?
  • thkala
    thkala almost 5 years
    @AnthonyRutledge: I am not aware of any edge cases, except for a script not actually using Bash by mistake. Even this works: let A=1; A+=1; echo $A - it prints 11, which is what you'd expect from a string concatenation operator. But I'd argue that if you are worried about edge cases, then you should probably use a "proper" programming language, maybe even one with a proper type system...
  • Sergio A.
    Sergio A. over 4 years
    @XXL surely I'd rather use brackets to encapsulate var's name. Highly recommended
  • Pynchia
    Pynchia over 4 years
    @F.Hauri thank you for pointing that out. But if you were to append a number, it would not work: e.g. echo $var2 does not produce myscript2
  • F. Hauri  - Give Up GitHub
    F. Hauri - Give Up GitHub over 4 years
    @Pynchia This work because of the dot . illegal in variable name. If else echo ${var}2 or see my answer
  • ulkas
    ulkas over 4 years
    quite a lot of characters. i would use rather $VAR1""$VAR2. this way i save the curved brackets
  • F. Hauri  - Give Up GitHub
    F. Hauri - Give Up GitHub about 4 years
    Interesting! Consider: join <(LANG=C bash -c 'a="a" c=1 last=${EPOCHREALTIME//.};while :;do a+=$a;now=${EPOCHREALTIME//.};echo $((c++)) ${#a} $((now-last));last=$now;done') <(LANG=C bash -c 'a="a" c=1 last=${EPOCHREALTIME//.};while :;do a=$a$a;now=${EPOCHREALTIME//.};echo $((c++)) ${#a} $((now-last));last=$now;done')|sed -ue '1icnt strlen a+=$a a=$a$a' -e 's/^\([0-9]\+\) \([0-9]\+\) \([0-9]\+\) \2/\1 \2 \3/' | xargs printf "%4s %11s %9s %9s\n" (Try this on not productive host!! ;)
  • Solx
    Solx almost 4 years
    I needed to use this += operator. I found that if I tried to put an underscore between the variables that bash would mistakenly think the name of the first variable included the underscore. Example if A=foo and B=bar, then $A$B is foobar, but $A_$B is only bar. Apparently thinks there is a variable named A_. Note that a dash works fine, meaning this case works as expected: $A-$B is foo-bar.
  • A. K.
    A. K. over 3 years
    This is the correct answer IMO, because i was looking for concatenating without spaces, and += works like a charm.
  • MadScientist
    MadScientist over 2 years
    @Solx you have to use {} if your variable reference is followed by another character that might be part of the variable name. Just write ${A}_$B and it will work fine in all shells.
  • alper
    alper over 2 years
    Can I also use echo -e if I want to have newline in between strings as: c="${a}\n${b}"
  • F. Hauri  - Give Up GitHub
    F. Hauri - Give Up GitHub about 2 years
    Playing with strings, see my function shrinkStr in Head & tail string in one line - possible?