如何在Bash中连接字符串变量


2765

在PHP中,字符串按以下方式串联在一起:

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

在这里,$foo成为“ Hello World”。

如何在Bash中完成?


6
foo="Hello" foo=$foo" World" echo $foo 这对于“#!/ bin / sh”
很有用

1
如果您希望HelloWorld没有空间怎么办?
阿迪

@Adifoo1="World" foo2="Hello" foo3="$foo1$foo2"
GeneCode

空格对bash
有影响

Answers:


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

通常,要串联两个变量,可以一个接一个地写它们:

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

314
$foo确实很重要的时期,养成双引号的习惯可能很好。
卡斯卡贝尔

104
我们被教导总是这样做,因为当替换发生时,shell将忽略空格,但是双引号将始终保护这些空格。
草莓

62
您的第一个示例中是否必须有空格?有可能做类似的事情foo="$fooworld"吗?我会假设没有...
不客气2014年

341
@nonsensickle那会寻找一个名为的变量fooworld。消除使用括号的歧义,例如foo="${foo}world"...
twalberg 2014年

4
@ JVE999是的,它也可以工作,尽管在我看来,代码清晰度并没有那么好...但这可能只是我的偏爱...还有其他几种方法也可以完成-重点是确保变量名称与非变量名称部分分开,以便正确解析。
twalberg 2014年

1127

Bash还支持+=以下代码所示的运算符:

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

2
我可以将此语法与export关键字一起使用吗?例如export A+="Z",也许A变量只需要导出一次?
levesque 2014年

3
@levesque:都:-)。变量只需要导出一次,但是export A+=Z效果也很好。
thkala 2014年

38
由于这是一种bashism,因此我认为值得一提的是,您永远不要#!/bin/sh在使用此构造的脚本中使用。
2015年

2
它是专门的,并且只是一个加等于运算符。也就是说,与Javascript不同,在Bash中,echo $ A + $ B打印“ X Y + Z”
phpguru

6
bashism是一种外壳功能,仅在bash某些其他更高级的外壳中受支持。它不能在busybox shdash/bin/sh很多发行版上)或某些其他Shell(如/bin/shFreeBSD上提供的外壳)下工作。
Score_Under

959

重击第一

正如这个问题专门代表Bash一样,我的答案的第一部分将提出正确执行此操作的不同方法:

+=:追加到变量

语法+=可以以不同的方式使用:

附加到字符串 var+=...

(因为我节俭,我只会用两个变量foo,并a在整个答案,然后再使用相同的。;-)

a=2
a+=4
echo $a
24

使用堆栈溢出问题语法,

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

工作良好!

附加到整数 ((var+=...))

变量a是一个字符串,也是一个整数

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

追加到数组 var+=(...)

我们a也是只有一个元素的数组。

echo ${a[@]}
36

a+=(18)

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

请注意,在括号之间有一个以空格分隔的array。如果要在数组中存储包含空格的字符串,则必须将它们括起来:

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

嗯.. 这不是bug,而是功能 ...为了防止bash尝试开发!",您可以:

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:使用内置命令重建变量

printf 内建命令给出绘制字符串格式的一种强有力的方式。由于这是Bash 内置的,因此可以选择将格式化的字符串发送到变量,而不是在以下位置打印stdout

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

此数组中有七个字符串。因此,我们可以构建一个格式化的字符串,其中包含恰好七个位置参数:

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

或者我们可以使用一个参数格式字符串,该字符串将与提交的许多参数重复...

注意,我们a仍然是一个数组!仅第一个元素被更改!

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!")'

在bash下,当您访问变量名称而不指定索引时,您始终仅寻址第一个元素!

因此,要检索我们的七个字段数组,我们只需要重新设置第一个元素:

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!")'

一个参数格式字符串,其中许多参数传递给:

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

使用堆栈溢出问题语法:

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

NOTA:使用双引号可以是用于操作包含字符串有用spacestabulations和/或newlines

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

现在壳

POSIX shell下,您不能使用bashisms,因此没有内置 函数printf

基本上

但是您可以简单地执行以下操作:

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

格式化,使用分叉 printf

如果您想使用更复杂的结构,则必须使用fork(新的子进程来完成工作,并通过返回结果stdout):

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

从历史上看,您可以使用反引号来检索fork的结果:

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

但这对于嵌套来说并不容易:

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

如果使用反引号,则必须使用反斜线转义内叉:

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

4
+=运营商也比快得多$a="$a$b"在我的测试中..这是有道理的。
马特

7
这个答案很棒,但我认为var=${var}.sh其他答案都缺少该示例,这非常有用。
祖纳拉玛

1
bash唯一能与外壳+=运营商?我想看看它是否具有足够的便携性
2016年

1
@dashesy号 我当然不是唯一一个带有+=操作符的shell ,但是所有这些方式都是bashisms,所以不是可移植的!甚至在bash版本错误的情况下也可能遇到特殊错误!
F. Hauri

134

你也可以这样做:

$ var="myscript"

$ echo $var

myscript


$ var=${var}.sh

$ echo $var

myscript.sh

4
尽管没有特殊的字符,也没有使用空格,但是双引号,引号和大括号没用:var=myscript;var=$var.sh;echo $var会产生相同的效果(在bash,破折号,busybox等环境下有效)。
F. Hauri

@ F.Hauri感谢您指出这一点。但是,如果要附加一个数字,它将不起作用:例如echo $var2,不会产生myscript2
Pynchia

@Pynchia这项工作是因为.变量名中的点非法。否则,echo ${var}2还是看到我的答案
F. Hauri

119
bla=hello
laber=kthx
echo "${bla}ohai${laber}bye"

将输出

helloohaikthxbye

$blaohai 导致找不到变量错误时,这很有用 。或者,如果字符串中包含空格或其他特殊字符。"${foo}"正确地转义您放入其中的所有内容。


3
不起作用 我从bash获得“ backupstorefolder:找不到命令”,其中“ backupstorefolder”是变量的名称。
Zian Choy

7
这有助于语法高亮显示很多,并消除了一些人为的歧义。
Ray Foss 2015年

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

     


10
这是shell脚本最有用的答案。我发现自己最近30分钟是因为我在等号前后都有一个空格!
Stefan 2013年

8
foo =“ $ {foo} World”
XXL

@XXL当然,我宁愿使用方括号来封装var的名称。强烈推荐
Sergio A.

33

我解决问题的方法就是

$a$b

例如,

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

产生

Hello World

例如,如果您尝试将一个字符串与另一个字符串连接起来,

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

然后echo "$c"会产生

Hello World

有额外的空间。

$aWorld

正如您所想像的那样不起作用,但是

${a}World

产生

HelloWorld

1
...因此${a}\ World产生Hello World
XavierStuvw

这让我感到惊讶;我曾期望c=$a$b在这里做与c=$a World(将尝试World作为命令运行)相同的事情。我想这意味着在扩展变量之前会先解析分配
。.– mwfearnley

30

这是大多数答案在说的简短摘要。

假设我们有两个变量,并且$ 1设置为'one':

set one two
a=hello
b=world

下表说明了可以结合的值ab创建新变量的不同上下文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

一些注意事项:

  • 将作业的RHS用双引号引起来通常是一个好习惯,尽管在很多情况下它是可选的
  • += 从性能的角度来看,如果以小增量(尤其是在循环中)构造大字符串,则效果更好
  • 使用{}变量名来消除它们的扩展歧义(如上表中的第2行)。从第3行和第4行可以看出,{}除非将变量与以一个字符开头的字符串连接在一起,该字符是shell变量名称中有效的第一个字符,即字母或下划线,否则无需这样做。

也可以看看:


2
如果您担心性能,请参阅我的答案中的分析stackoverflow.com/a/47878161/117471
Bruno Bronosky


20

还有另一种方法...

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

...还有一个

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

1
这就是我所做的,并且我发现它比其他答案更加简单明了。是否有理由没有人在投票最多的答案上指出此选项?
quimnuss

1
@quimnuss字符串与OP问题中使用的字符串不匹配的事实可能是一个很好的原因。
jlliagre

20

如果要附加下划线,请使用转义(\)

FILEPATH=/opt/myfile

这并不能正常工作:

echo $FILEPATH_$DATEX

这工作正常:

echo $FILEPATH\\_$DATEX

11
或者,$ {FILEPATH} _ $ DATEX。这里的{}用于指示变量名的边界。这是适当的,因为下划线是变量名中的合法字符,因此在您的
摘要中

1
对我来说,我旁边有一个变量,即$ var1和常量,因此echo $ var1_costant_traling_part对我
有用

2
我想逃脱只需要一个强烈的反响:echo $a\_$b会的。正如Nik O'Lai的评论所暗示的,下划线是常规字符。对于字符串,回显和串联,空白的处理要敏感得多- \ 随着此问题不时出现,人们可以彻底使用和读取此线程。
XavierStuvw

16

带引号的最简单方法:

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

1
引号太多,恕我直言。var=$B$b"a"; echo Hello\ $var我相信会做到的
XavierStuvw

我建议使用所有引号,因为如果将其放在任何地方都不会错过,则不必思考。
betontalpfa

15

您可以串联而不使用引号。这是一个例子:

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

最后一条语句将打印“ OpenSystems”(不带引号)。

这是一个Bash脚本的示例:

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

1
第一块的语法令人困惑。这些$符号是什么意思?
XavierStuvw

15

即使现在允许+ =运算符,它也在2004年的Bash 3.1中引入。

如果运气好的话,在较早的Bash版本上使用此运算符的任何脚本都将失败,并显示“找不到命令”错误,或者出现“意外令牌附近的语法错误”。

对于那些关心向后兼容性的人,请遵循较早的标准Bash串联方法,如所选答案中提到的那些方法:

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

1
感谢您指出这一点,我只是在搜索该功能需要哪个版本。
罗披

14

我更喜欢使用大括号${}来扩展字符串中的变量:

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

花括号适合连续使用的字符串:

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

否则,foo = "$fooWorld"将无法使用。


8

如果你正在尝试做的是分割字符串成几行,你可以用一个反斜杠:

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

中间有一个空格:

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

这两个之间也仅添加一个空格:

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

恐怕这不是什么意思
installero

7

更安全的方法:

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

包含空格的字符串可以成为命令的一部分,请使用“ $ XXX”和“ $ {XXX}”来避免这些错误。

再看一下关于+ =的其他答案


在定义点显示带有命令读取的带有空格的字符串的点。因此d=DD DDDD: command not found请注意---是最后一个DD,而不是找不到的d。如果所有操作数的格式正确,并且已经包含必需的空格,则可以简单地将s=${a}${b}${c}${d}; echo $s,与串联,并使用较少的引号。您也可以使用\ (转义的空格)来避免这些问题--- d=echo\ echo不会启动任何回声调用d=echo echo
XavierStuvw

7

在一种特殊情况下,您应该注意:

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

将输出"daniel"san,而不是danielsan您可能想要的。在这种情况下,您应该改为:

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

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

这是连接两个字符串的方式。


1
这行得通,但是由于变量插值不受保护,因此有时会产生不可预测的结果。因此,您不能在所有用例上都依赖此表格。
安东尼·鲁特里奇

5

如果这是您添加" World"到原始字符串的示例,则可以是:

#!/bin/bash

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

输出:

Hello World

5
var1='hello'
var2='world'
var3=$var1" "$var2 
echo $var3

2
var3=$var1\ $var2具有相同的效果
XavierStuvw

5

人们对性能表示担忧,但是没有提供任何数据。让我建议一个简单的测试。

(注意:date在macOS上不提供纳秒级,因此必须在Linux上完成。)

在GitHub上创建了append_test.sh,内容如下:

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

测试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

测试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

该错误表明我的Bash 在崩溃前已达到335.54432 MB。您可以将代码从加倍数据更改为附加常量,以获取更精细的图形和故障点。但是我认为这应该给您足够的信息来决定您是否在乎。就个人而言,我不超过100 MB。你的旅费可能会改变。


有趣!考虑:(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"
非生产性

4

我想从列表中构建一个字符串。找不到答案,所以我在这里发布。这是我所做的:

list=(1 2 3 4 5)
string=''

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

echo ${string}

然后我得到以下输出:

1 2 3 4 5

4

尽管有特殊的运算符+=进行连接,但还有一种更简单的方法:

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

双引号需要额外的计算时间来解释内部变量。尽可能避免它。


3

请注意,这行不通

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

因为它似乎会丢弃$ foo并留下:

PREFIX_WORLD

但这可以工作:

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

并为您提供正确的输出:

PREFIX_HELLO_WORLD


8
发生这种情况是因为下划线是变量名中的有效字符,因此bash将foo_视为变量。当需要告诉bash确切的var名称边界时,可以使用花括号:PREFIX _ $ {foo} _ $ bar
Nik O'Lai 2014年


1

方便时,我这样做:使用内联命令!

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

1
在第一行,您可以使用:代替来放下叉子。在最近的狂欢中,您可以编写:。date "+The current time is %a %b %d %Y +%T"echo ...$(date)printf "The current time is %(%a %b %d %Y +%T)T\n" -1
F. Hauri 2014年

1

我认为,连接两个字符串的最简单方法是编写一个为您执行此操作的函数,然后使用该函数。

function concat ()
{
    prefix=$1
    suffix=$2

    echo "${prefix}${suffix}"
}

foo="Super"
bar="man"

concat $foo $bar   # Superman

alien=$(concat $foo $bar)

echo $alien        # Superman

-1

我有点喜欢快速功能。

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

echo $(combo 'foo''bar')

换皮猫的另一种方式。这次带功能:D


在子shell中调用一个函数,对于简单的concat来说,这太过分了。
codeforester

-1

我还不了解PHP,但这在Linux Bash中有效。如果您不想将其影响到变量,则可以尝试以下操作:

read pp;  *# Assumes I will affect Hello to pp*
pp=$( printf $pp ;printf ' World'; printf '!');
echo $pp;

>Hello World!

您可以放置​​另一个变量,而不是“ Hello”或“!”。您也可以连接更多字符串。


2
使用子shell进行简单的连接太昂贵了!
codeforester
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.