In PHP, strings are concatenated together as follows:
$foo = "Hello";
$foo .= " World";
Here, $foo
becomes "Hello World"
.
How is this accomplished 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}"
> 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
foo="$fooworld"
? I would assume not... –
Echinate fooworld
. Disambiguating that is done with braces, as in foo="${foo}world"
... –
Dispel foo=$foo'world'
–
Blanks 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'
. –
Varick $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. –
Tache echo 'User Name: $USER'
–
Aspen 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 –
Ayacucho =
! This will not work: a ='hello'
, neither will this: a = ' hello'
, etc. –
Agonic 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
. –
Valse $VAR1""$VAR2
. this way i save the curved brackets –
Luster echo -e
if I want to have newline in between strings as: c="${a}\n${b}"
–
Fulbright Bash also supports a +=
operator as shown in this code:
A="X Y"
A+=" Z"
echo "$A"
output
X Y Z
export A+="Z"
or maybe the A
variable only needs to be exported once? –
Realm export A+=Z
works quite nicely as well. –
Thump #!/bin/sh
in a script using this construction. –
Delanty 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. –
Delanty +=
form does not hit an edge case? –
Lumbering 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... –
Thump {}
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. –
Te As this question stand specifically for Bash, my first part of the answer would present different ways of doing this properly:
+=
: Append to variableThe syntax +=
may be used in different ways:
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!
((var+=...))
variable a
is a string, but also an integer
echo $a
24
((a+=12))
echo $a
36
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' hello\ world\!)
declare -p a
declare -a a='([0]="36" [1]="18" [2]="one" [3]="word" [4]="hello world!" [5]="h
ello world!" [6]="hello world!" [7]="hello world!")'
Where 6 fields (one
, word
and 4 x hello world!
), where added to array ${ar[@]}
which already contained 38
and 18
.
printf
: Re-construct variable using the builtin commandThe 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!>
<hello world!>
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"
Under POSIX shell, you could not use bashisms, so there is no builtin printf
.
But you could simply do:
foo="Hello"
foo="$foo World"
echo "$foo"
Hello World
In the other side:
foo=" World"
foo="Hello$foo"
echo "$foo"
Hello World
But if you want to add letters immediately after your variable, without space, you may need to use braces:
foo="Hello "
foo="${foo}World"
echo "$foo"
Hello World
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
Playing with strings, have a look at:
shrinkStr
will limit string length to specific length by taking begin and end of them. and headTail
will take head and tail of a file by building appropriate sed
command.+=
operator is also much faster than $a="$a$b"
in my tests.. Which makes sense. –
Richella var=${var}.sh
example from other answers, which is very useful. –
Fleurdelis bash
the only shell with +=
operator? I want to see if it is portable enough –
Gahl +=
operator, but all this ways are bashisms, so not portable! Even you could encounter special bug in case of wrong bash version! –
Closestool enclose
demo? –
Terranceterrane a+=(one word "hello world"! 'hello world!' $'hello world\041')
where 5 fields will be added to array "${a[@]}"
–
Closestool Shell now
... ( sorry for the delay 7y7m1d18h40m It's never too late to do well! ;-) –
Closestool You can do this too:
$ var="myscript"
$ echo $var
myscript
$ var=${var}.sh
$ echo $var
myscript.sh
var=myscript;var=$var.sh;echo $var
would have same effects (This work under bash, dash, busybox and others). –
Closestool echo $var2
does not produce myscript2
–
Alt .
illegal in variable name. If else echo ${var}2
or see my answer –
Closestool 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.
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
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:
+=
is better from a performance standpoint if a big string is being constructed in small increments, especially in a loop{}
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:
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
${a}\ World
produces Hello World
–
Brag 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.. –
Marvellamarvellous $ 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.
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
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. –
Brag The simplest way with quotation marks:
B=Bar
b=bar
var="$B""$b""a"
echo "Hello ""$var"
var=$B$b"a"; echo Hello\ $var
would do, I believe –
Brag 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
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
(i.e., 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.
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
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.
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 +=
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. –
Brag 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
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
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.
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!! ;) –
Closestool a="Hello,"
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:
#!/bin/bash
foo="Hello"
foo=$foo" World"
echo $foo
The output:
Hello World
var1='hello'
var2='world'
var3=$var1" "$var2
echo $var3
var3=$var1\ $var2
has the same effect –
Brag 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
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`"
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
. –
Closestool 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
There's also the possibility to use named references
in Bash 4.x, to add arbitrary strings to the referenced string (delimited by a referenced separator each):
delcare str='S1'
declare separator=','
# Appends to str ref #1, separated by sep ref #2
app_str() {
local -n str="$1"
local -n sep="$2"
local -a appends=("${@:3}")
local -i appsLen=${#appends[@]}
local -i i
# If any appends, add 1st sep to referenced str
if [[ $appsLen -gt 0 ]]; then
str+="${sep}"
fi
for appStr in "${appends[@]}"; do
str+="$appStr"
if [[ $((++i)) -lt $appsLen ]]; then
str+="${sep}"
fi
done
}
Use it like so:
app_str str separator 'S2' 'S3' 'S4'
echo -e "$str"
Another way to append (and separate) with `printf``:
declare separator=','
declare testStr='STR1'
declare -a testArr=(STR2 STR3 STR4)
testStr="$testStr"$(printf "$separator%s" "${testArr[@]}")
echo -e "$testStr"
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
© 2022 - 2024 — McMap. All rights reserved.
foo="Hello"
foo=$foo" World"
echo $foo
this rather worked for "#!/bin/sh" – Caressafoo1="World" foo2="Hello" foo3="$foo1$foo2"
– Nathalienathanecho "sh ${HOME}/ultimate-utils/run_tb.sh"
– Untouchable