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

3510
在PHP中,字符串可以通过以下方式进行拼接:
$foo = "Hello";
$foo .= " World";

在这里,$foo 变成了 "Hello World"

在Bash中如何实现这个操作?


15
foo = "Hello" foo = $foo" World" echo $foo这对于“#!/bin/sh”而言是有效的。 - parasrish
1
如果您想要没有空格的HelloWorld,该怎么办? - Adi
2
@Adi foo1="世界" foo2="你好" foo3="$foo2$foo1" - GeneCode
2
在 Bash 中,空格是有影响的。 - Capibar
1
举个插入字符串的例子,可以这样写:echo "sh ${HOME}/ultimate-utils/run_tb.sh" - Charlie Parker
30个回答

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

通常,将两个变量连接在一起只需将它们写在一起即可:

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

80
你的第一个例子中必须要有空格吗?像 foo="$fooworld" 这样做是否可行?我猜不行... - nonsensickle
393
@nonsensickle 这会查找名为fooworld的变量。使用大括号可以消除歧义,例如foo="${foo}world"... - twalberg
5
@twalberg 我发现你也可以使用 foo=$foo'world' - JVE999
6
@JVE999,是的,那也可以,但在我看来,它不太有利于代码清晰度... 但这可能只是我的个人喜好... 还有几种其他的方法可以实现 - 关键是确保变量名与非变量名部分分开,以便正确解析。 - twalberg
3
将整个字符串用双引号括起来的问题在于,如果附加字符串中包含变量名等内容,则Shell会将其解释,这通常是不希望发生的。在我看来,常见情况需要使用语法$foo="$foo"' world' - Mason Heller
显示剩余10条评论

1343

Bash也支持+=运算符,就像这段代码所示:

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

输出

X Y Z


2
我可以在export关键字中使用这种语法吗?例如 export A+="Z" 或者也许只需要导出一次 A 变量? - levesque
4
@levesque: 两者皆可 :-). 变量只需要导出一次,但 export A+=Z 也可以很好地工作。 - thkala
63
因为这是一种Bash特有的语法,所以我认为值得提醒的是,在使用这种语句结构的脚本中,永远不要使用 #!/bin/sh - Score_Under
3
它是一个特定且仅限于加等号运算符。也就是说,与JavaScript不同,在Bash中,echo $A+$B会输出"X Y+Z"。 - phpguru
11
"bashism" 是仅在 bash 和某些其它高级 shell 中支持的一种 shell 特性。它在 busybox shdash(在许多发行版上是 /bin/sh),以及像 FreeBSD 提供的 /bin/sh 这样的其它 shell 中无法运行。 - Score_Under
显示剩余5条评论

1094

先来Bash

由于这个问题特别涉及到Bash,我的回答的第一部分将介绍正确执行此操作的不同方法:

+=:追加到变量

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

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

(因为我很节俭,所以我只使用了两个变量fooa,然后在整个回答中重复使用它们。;-)

a=2
a+=4
echo $a
24
使用Stack Overflow问题语法,
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
请注意,括号内是一个以空格分隔的数组。如果您想在数组中存储包含空格的字符串,您需要将其括起来:
a+=(one word "hello world!" )
bash: !": event not found
嗯.. 这不是一个错误,而是一个特性... 为了防止bash尝试开发!",你可以:
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!")'

6个字段(oneword和4个hello world!)被添加到数组${ar[@]}中,该数组已经包含3818

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!>
<hello world!>
使用Stack Overflow问题语法:
foo="Hello"
printf -v foo "%s World" $foo
echo $foo
Hello World
注意:使用“双引号”可以方便地处理包含空格、制表符和/或换行符的字符串。
printf -v foo "%s World" "$foo"

现在的Shell

POSIX shell下,你不能使用bashisms,所以没有内建printf命令。

基本上

但你可以简单地这样做:

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

使用格式化的forked 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

9
在我的测试中,“+=” 运算符比 “$a = "$a$b"” 更快。这是有道理的。 - Matt
10
这个答案很不错,但我认为它缺少其他答案中的var=${var}.sh示例,这个示例非常有用。 - geneorama
1
bash 是唯一具有 += 运算符的 shell 吗?我想看看它是否足够可移植。 - dashesy
1
@dashesy 不是唯一一个带有 += 运算符的 shell,但这些方法都是 Bashism,所以不具备可移植性!即使使用 Bash 版本错误,你也会遇到特殊的错误! - F. Hauri - Give Up GitHub
1
这是我个人认为的正确答案,因为我想要不带空格的连接,而 += 的效果非常好。 - A. K.
显示剩余3条评论

139

你也可以这样做:

$ var="myscript"

$ echo $var

myscript


$ var=${var}.sh

$ echo $var

myscript.sh

6
虽然没有使用特殊字符或空格,但双引号、单引号和花括号都是无用的:在bash、dash、busybox等环境下,var=myscript;var=$var.sh;echo $var会产生相同的效果。 - F. Hauri - Give Up GitHub
@F.Hauri 谢谢您指出这一点。但是,如果您附加一个数字,它将不起作用:例如echo $var2不能生成myscript2 - Pynchia
@Pynchia,这是因为在变量名中使用点“.”是不合法的。如果要打印变量,可以使用 if else 语句并输出 ${var}2 或者看一下 我的回答 - F. Hauri - Give Up GitHub

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

将输出

helloohaikthxbye

$blaohai导致变量未找到错误时,此方法非常有用。或者如果您的字符串中有空格或其他特殊字符,则"${foo}"会正确转义您输入的任何内容。


3
不工作。我从bash中得到“backupstorefolder: 命令未找到”的信息,其中“backupstorefolder”是一个变量的名称。 - Zian Choy
8
这可以大大提高语法高亮的效果,并消除一些人为不确定性。 - Ray Foss

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

     


13
这是关于Shell脚本最实用的答案。我在过去30分钟里一直在寻找原因,因为我在等号前后加了一个空格!! - Stefan
11
foo="${foo}World"的翻译是:将变量foo的值附加上字符串"World"。 - XXL
@XXL 我肯定更愿意使用括号来封装变量的名称。强烈推荐。 - Sergio A.

44

这里是大多数答案讨论的简要概述。

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

set one two
a=hello
b=world
下表解释了不同的上下文,在这些上下文中我们可以组合变量ab的值创建新变量c
ContextCalculation
Context 1c = a + b
Context 2c = a - b
Context 3c = a * b
Context 4c = a / b
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

几点注意事项:

  • 将赋值语句右侧的内容用双引号括起来通常是一个好习惯,尽管在许多情况下这是可选的
  • 如果在小块逐步构建大字符串,特别是在循环中,则从性能方面考虑,使用+=更好
  • 在变量名周围使用{}以消除其扩展的歧义(如上表第2行)。 正如第3行和第4行所示,在与以字母或下划线开头的字符开始的字符串连接变量时,没有必要使用{}

另请参见:


2
如果您关心性能,请查看我的答案中的分析 https://dev59.com/0m855IYBdhLWcg3wuG0M#47878161 - Bruno Bronosky

38

我解决问题的方法只是

$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

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

22

另一种方法...

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

......而又出现了另一个。

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

1
这就是我所做的,我发现这比其他答案简单得多,直接明了。难道没有人在最受欢迎的答案中指出这个选项的原因吗? - quimnuss
1
@quimnuss 字符串与 OP 问题中使用的不匹配可能是一个很好的理由。 - jlliagre

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,