ChatGPT解决这个技术问题 Extra ChatGPT

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?

foo="Hello" foo=$foo" World" echo $foo this rather worked for "#!/bin/sh"
What to do if you want HelloWorld without space?
@Adi foo1="World" foo2="Hello" foo3="$foo1$foo2"
spaces does a matter in bash)
to give an example of inserting into a string do echo "sh ${HOME}/ultimate-utils/run_tb.sh"

P
Potherca
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

Does there have to be a space in your first example? Is it possible to do something like foo="$fooworld"? I would assume not...
@nonsensickle That would look for a variable named fooworld. Disambiguating that is done with braces, as in foo="${foo}world"...
@twalberg I've found you can also use foo=$foo'world'
@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.
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'.
K
Kasun Siyambalapitiya

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

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

output

X Y Z


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?
@levesque: Both :-). Variables only need to be exported once, but export A+=Z works quite nicely as well.
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.
It's specifically and only a plus-equals operator. That is, unlike Javascript, in Bash, echo $A+$B prints "X Y+Z"
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.
J
Jason Aller

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

The += operator is also much faster than $a="$a$b" in my tests.. Which makes sense.
This answer is awesome, but I think it's missing the var=${var}.sh example from other answers, which is very useful.
Is bash the only shell with += operator? I want to see if it is portable enough
@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!
This is the correct answer IMO, because i was looking for concatenating without spaces, and += works like a charm.
u
userend

You can do this too:

$ var="myscript"

$ echo $var

myscript


$ var=${var}.sh

$ echo $var

myscript.sh

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 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
@Pynchia This work because of the dot . illegal in variable name. If else echo ${var}2 or see my answer
o
orkoden
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.


Doesn't work. I get "backupstorefolder: command not found" from bash where "backupstorefolder" is the name of a variable.
This helps syntax highlighting quite a bit, and removes some human ambiguity.
P
Peter Mortensen
foo="Hello "
foo="$foo World"


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!!
foo="${foo}World"
@XXL surely I'd rather use brackets to encapsulate var's name. Highly recommended
c
codeforester

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:

BashFAQ/013 - How can I concatenate two variables?

When do we need curly braces around shell variables?


If you are concerned about performance, see an analysis in my answer stackoverflow.com/a/47878161/117471
A
Alfe

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

...hence ${a}\ World produces Hello World
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..
b
bcosca
$ a=hip
$ b=hop
$ ab=$a$b
$ echo $ab
hiphop
$ echo $a$b
hiphop

A
Akseli Palén

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.

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?
@quimnuss The fact the strings do not match those used in the OP question might be a good reason.
S
Sangram Shivankar

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

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
for me I had one variable i.e. $var1 and constant next to this, so echo $var1_costant_traling_part works for me
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.
P
Peter Mortensen

The simplest way with quotation marks:

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

Too many quotation marks, IMHO. var=$B$b"a"; echo Hello\ $var would do, I believe
I suggest to use all of the quotation marks, cause if you put it everywhere you cannot miss, you dont have to think.
P
Peter Mortensen

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

Thanks for pointing this out, I was just searching for which version is require for this to work.
P
Peter Mortensen

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

The syntax of the first block is confusing. What do these $ signs mean?
N
Nick Tsai

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.


P
Peter Mortensen

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.


j
jcarballo

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

I'm afraid this is not what was meant
d
dan

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.


B
Bohdan

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 +=


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.
d
diogovk

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

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

This is how you concatenate two strings.


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.
n
nonopolarity

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

h
hari
var1='hello'
var2='world'
var3=$var1" "$var2 
echo $var3

Also var3=$var1\ $var2 has the same effect
P
Peter Mortensen

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.


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!! ;)
s
simibac

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

D
Dss

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


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
P
Peter Mortensen

Here is the one through AWK:

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

Nice, But I think I could obtain more precision by using Python !
M
Marty

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

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

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 .
A
Anthony Rutledge

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

C
CommaToast

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


Calling a function, that too in a subshell, is too much an overkill for a simple concat.