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

Str*_*rry 2624 syntax bash shell concat string-concatenation

在PHP中,字符串连接在一起如下:

$foo = "Hello";
$foo .= " World";
Run Code Online (Sandbox Code Playgroud)

在这里,$foo成为"Hello World".

在Bash中如何实现?

cod*_*ict 3540

foo="Hello"
foo="${foo} World"
echo "${foo}"
> Hello World
Run Code Online (Sandbox Code Playgroud)

通常,要连接两个变量,您可以一个接一个地编写它们:

a='Hello'
b='World'
c="${a} ${b}"
echo "${c}"
> Hello World
Run Code Online (Sandbox Code Playgroud)

  • @nonsensickle那会找一个名为`fooworld`的变量.使用大括号消除歧义,如`foo ="$ {foo} world"`... (336认同)
  • 将'$ foo`放在双引号内的习惯可能很好,因为它确实很重要. (301认同)
  • 我们被教导要始终这样做,因为当替换发生时,shell将忽略空格,但双引号将始终保护这些空格. (100认同)
  • 你的第一个例子中是否必须有空格?有可能做一些像`foo ="$ fooworld"`的事情吗?我不认为...... (62认同)
  • @ JVE999是的,这也有效,虽然在我看来它对代码清晰度来说并不是那么好......但这可能仅仅是我的偏好......还有其他一些方法可以做到 - 重点是确保变量名称与非变量名称部分分开,以便正确解析. (4认同)
  • 将整个事物放在双引号中的一个问题,如`foo ="$ foo World",就是如果它包含变量名等,那么附加字符串(在本例中为"world")将由shell解释,这通常是不想要的.IMO,常见的情况需要语法`$ foo ="$ foo"'world'. (3认同)
  • @twalberg我发现你也可以使用`foo = $ foo'world' (2认同)

thk*_*ala 1075

Bash还支持+=运算符,如下代码所示:

$ A="X Y"
$ A+=" Z"
$ echo "$A"
X Y Z
Run Code Online (Sandbox Code Playgroud)

  • 由于这是一种基础,我认为值得一提的是,你不应该在使用这种结构的脚本中使用`#!/ bin/sh`. (31认同)
  • bashism是一个shell特性,仅在`bash`和某些其他更高级的shell中受支持.它不会在`busybox sh`或`dash`(很多发行版上的`/ bin/sh`)或FreeBSD上提供的某些其他shell(如`/ bin/sh`)下工作. (6认同)
  • @levesque:两个:-).变量只需要导出一次,但`export A + = Z`也可以很好地导出. (3认同)
  • 我可以在export关键字中使用此语法吗?例如`export A + ="Z"`或者'A`变量只需要导出一次? (2认同)
  • 它是专门的,并且只是一个加等于运算符。也就是说,与Javascript不同,在Bash中,echo $ A + $ B打印“ X Y + Z” (2认同)

F. *_*uri 930

Bash第一

由于这个问题专门针对Bash,我的答案的第一部分将提供不同的方法来正确地做到这一点:

+=:附加到变量

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

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

(因为我节俭,我只会用两个变量fooa,然后在整个答案重复使用相同的;-).

a=2
a+=4
echo $a
24
Run Code Online (Sandbox Code Playgroud)

使用Stack Overflow问题语法,

foo="Hello"
foo+=" World"
echo $foo
Hello World
Run Code Online (Sandbox Code Playgroud)

工作良好!

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

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

echo $a
24
((a+=12))
echo $a
36
Run Code Online (Sandbox Code Playgroud)

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

我们a也是一个只有一个元素的阵列.

echo ${a[@]}
36

a+=(18)

echo ${a[@]}
36 18
echo ${a[0]}
36
echo ${a[1]}
18
Run Code Online (Sandbox Code Playgroud)

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

a+=(one word "hello world!" )
bash: !": event not found
Run Code Online (Sandbox Code Playgroud)

嗯...... 这不是一个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!")'
Run Code Online (Sandbox Code Playgroud)

printf:使用builtin命令重新构造变量

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

echo ${a[@]}
36 18 one word hello world! hello world! hello world!
Run Code Online (Sandbox Code Playgroud)

这个数组中有七个字符串.所以我们可以构建一个包含七个位置参数的格式化字符串:

printf -v a "%s./.%s...'%s' '%s', '%s'=='%s'=='%s'" "${a[@]}"
echo $a
36./.18...'one' 'word', 'hello world!'=='hello world!'=='hello world!'
Run Code Online (Sandbox Code Playgroud)

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

请注意,我们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!")'
Run Code Online (Sandbox Code Playgroud)

在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!")'
Run Code Online (Sandbox Code Playgroud)

传递给许多参数的一个参数格式字符串:

printf -v a[0] '<%s>\n' "${a[@]}"
echo "$a"
<36>
<18>
<one>
<word>
<hello world!>
<hello world!>
<hello world!>
Run Code Online (Sandbox Code Playgroud)

使用Stack Overflow问题语法:

foo="Hello"
printf -v foo "%s World" $foo
echo $foo
Hello World
Run Code Online (Sandbox Code Playgroud)

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

printf -v foo "%s World" "$foo"
Run Code Online (Sandbox Code Playgroud)

壳牌现在

POSIX shell下,你不能使用bashisms,所以没有内置 printf.

基本上

但你可以这样做:

foo="Hello"
foo="$foo World"
echo $foo
Hello World
Run Code Online (Sandbox Code Playgroud)

格式化,使用分叉 printf

如果你想使用更复杂的结构,你必须使用fork(创建作业的新子进程并通过它返回结果stdout):

foo="Hello"
foo=$(printf "%s World" "$foo")
echo $foo
Hello World
Run Code Online (Sandbox Code Playgroud)

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

foo="Hello"
foo=`printf "%s World" "$foo"`
echo $foo
Hello World
Run Code Online (Sandbox Code Playgroud)

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

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

使用反斜杠,你必须使用反斜杠逃避内叉:

foo="Today is: "
foo=`printf "%s %s" "$foo" "\`date\`"`
echo $foo
Today is: Sun Aug 4 11:59:10 CEST 2013
Run Code Online (Sandbox Code Playgroud)

  • 这个答案很棒,但我认为它缺少来自其他答案的`var = $ {var} .sh`示例,这非常有用. (7认同)
  • 在我的测试中,`+ =`运算符也比`$ a ="$ a $ b"`快得多..这是有道理的. (4认同)

use*_*end 133

你也可以这样做:

$ var="myscript"

$ echo $var

myscript


$ var=${var}.sh

$ echo $var

myscript.sh
Run Code Online (Sandbox Code Playgroud)

  • 虽然没有使用特殊的字符,也没有使用空格,但双引号,引号和大括号都是无用的:`var = myscript; var = $ var.sh; echo $ var`会产生相同的效果(这项工作在bash,dash,busybox和其他). (4认同)

ork*_*den 117

bla=hello
laber=kthx
echo "${bla}ohai${laber}bye"
Run Code Online (Sandbox Code Playgroud)

会输出

helloohaikthxbye
Run Code Online (Sandbox Code Playgroud)

$blaohai 导致变量未找到错误时,这很有用 .或者如果字符串中有空格或其他特殊字符."${foo}"适当地逃避你投入的任何东西.

  • 这有助于语法突出显示,并消除了一些人为的歧义. (7认同)
  • 不行.我从bash获得"backupstorefolder:command not found",其中"backupstorefolder"是变量的名称. (3认同)

vin*_*hkr 44

foo="Hello "
foo="$foo World"
Run Code Online (Sandbox Code Playgroud)

     

  • 这是shell脚本最有用的答案.我发现自己是最后30分钟因为我在等号之前和之后都有空位!! (9认同)
  • 富= "$ {} foo的世界" (8认同)

Chr*_*ith 32

我解决问题的方法就是

$a$b
Run Code Online (Sandbox Code Playgroud)

例如,

a="Hello"
b=" World"
c=$a$b
echo "$c"
Run Code Online (Sandbox Code Playgroud)

哪个产生

Hello World
Run Code Online (Sandbox Code Playgroud)

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

a="Hello"
c="$a World"
Run Code Online (Sandbox Code Playgroud)

然后echo "$c"会产生

Hello World
Run Code Online (Sandbox Code Playgroud)

有一个额外的空间.

$aWorld
Run Code Online (Sandbox Code Playgroud)

你可能想象不起作用,但是

${a}World
Run Code Online (Sandbox Code Playgroud)

产生

HelloWorld
Run Code Online (Sandbox Code Playgroud)


bco*_*sca 29

$ a=hip
$ b=hop
$ ab=$a$b
$ echo $ab
hiphop
$ echo $a$b
hiphop
Run Code Online (Sandbox Code Playgroud)


cod*_*ter 24

以下是大多数答案所讨论的简要总结.

假设我们有两个变量:

set one two
a=hello
b=world
Run Code Online (Sandbox Code Playgroud)

下表解释了我们可以组合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
Run Code Online (Sandbox Code Playgroud)

几点说明:

  • 用双引号括起赋值的RHS通常是一种很好的做法,尽管在很多情况下它是非常可选的
  • += 如果以较小的增量构建一个大字符串,尤其是在循环中,从性能角度来看会更好
  • 使用{}变量名称来消除其扩展的歧义(如上表中的第2行)

也可以看看:

  • 如果您担心性能,请参阅我的回答中的分析/sf/answers/3351471301/ (2认同)

小智 20

如果你想追加下划线之类的东西,请使用escape(\)

FILEPATH=/opt/myfile
Run Code Online (Sandbox Code Playgroud)

这并不能正常工作:

echo $FILEPATH_$DATEX
Run Code Online (Sandbox Code Playgroud)

这很好用:

echo $FILEPATH\\_$DATEX
Run Code Online (Sandbox Code Playgroud)

  • 或者,$ {FILEPATH} _ $ DATEX.这里{}用于表示变量名称的边界.这是合适的,因为下划线是变量名中的合法字符,所以在你的代码片段中bash实际上尝试解析FILEPATH_,而不仅仅是$ FILEPATH (11认同)
  • 我想只需要一次反弹就可以逃脱:`echo $ a\_ $ b`会做.正如Nik O'Lai的评论中暗示的那样,下划线是一个常规角色.处理空格对字符串,回声和连接更敏感 - 人们可以使用`\`并彻底阅读这个问题,因为这个问题偶尔会出现. (2认同)

Aks*_*lén 19

又一种方法......

> H="Hello "
> U="$H""universe."
> echo $U
Hello universe.
Run Code Online (Sandbox Code Playgroud)

......还有一个.

> H="Hello "
> U=$H"universe."
> echo $U
Hello universe.
Run Code Online (Sandbox Code Playgroud)


bet*_*pfa 16

引号最简单的方法:

B=Bar
b=bar
var="$B""$b""a"
echo "Hello ""$var"
Run Code Online (Sandbox Code Playgroud)

  • 引号太多了,恕我直言。`var=$B$b"a"; echo Hello\ $var` 可以,我相信 (2认同)

mar*_*fer 15

您可以在没有引号的情况下连接.这是一个例子:

$Variable1 Open
$Variable2 Systems
$Variable3 $Variable1$Variable2
$echo $Variable3
Run Code Online (Sandbox Code Playgroud)

最后一个语句将打印"OpenSystems"(没有引号).

这是Bash脚本的一个示例:

v1=hello
v2=world
v3="$v1       $v2"
echo $v3            # Output: hello world
echo "$v3"          # Output: hello       world
Run Code Online (Sandbox Code Playgroud)

  • 第一个块的语法令人困惑。这些 $ 符号是什么意思? (2认同)

小智 15

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

在较旧的Bash版本上使用此运算符的任何脚本都将失败,如果您幸运的话,"找不到命令"错误,或者"意外令牌附近的语法错误".

对于那些关心向后兼容性的人,请坚持使用旧的标准Bash级联方法,如所选答案中提到的那些:

foo="Hello"
foo="$foo World"
echo $foo
> Hello World
Run Code Online (Sandbox Code Playgroud)


dan*_*dan 15

默认情况下,Bash 中的变量和数组(索引或关联*)始终是字符串,但您可以使用内置标志declare,为它们提供“整数”( -i) 或“引用”** ( -n) 等属性,这会改变它们的方式表现。

Bash 算术接受 ASCII/字符串数字作为输入,因此几乎没有理由实际使用整数属性。

另外,变量值不能包含 ASCII NULL(即 8 位零),因为使用常规的空终止 C 字符串来实现它们。

* 即一个或多个键+值对。
** 引用变量扩展为另一个变量的值,该变量的标签被分配给引用变量

附加一个字符串:

$ foo=Hello
$ foo+=' world!'
$ echo "$foo"

Hello world!

$ num=3
$ num+=4
echo "$num"

34 # Appended string (not a sum)
Run Code Online (Sandbox Code Playgroud)

使用整数属性的少数原因之一是它改变了赋值运算符的行为+=

$ declare -i num=3
$ num+=4
echo "$num"

7 # Sum
Run Code Online (Sandbox Code Playgroud)

请注意,这不适用于-=、等,除非您在算术 (和)/=内执行此操作,其中无论有或没有整数属性,数字都已被视为相同。有关这些运算符的完整列表,请参阅“算术评估”部分,这些运算符与 C 语言相同。(( ))$(( ))man bash

赋值运算符+=还可以用于将新元素附加到索引数组(也称为“列表”):

$ foo=(one)
$ foo+=(two)
$ printf 'Separate element: %s\n' "${foo[@]}"

Separate element: one
Separate element: two
Run Code Online (Sandbox Code Playgroud)

另一种常见的方法是使用计数器:

$ foo[c++]=one
$ foo[c++]=two
Run Code Online (Sandbox Code Playgroud)

POSIX shell 不使用赋值+=运算符来附加字符串,因此您必须这样做:

$ foo=Hello
$ foo="$foo world!"
$ echo "$foo"

Hello world!
Run Code Online (Sandbox Code Playgroud)

这在 Bash 中也很好,因此它可以被认为是一种更可移植的语法。


Nic*_*sai 13

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

foo="Hello"
foo="${foo} World"
echo $foo
> Hello World
Run Code Online (Sandbox Code Playgroud)

卷曲括号适合连续字符串使用:

foo="Hello"
foo="${foo}World"
echo $foo
> HelloWorld
Run Code Online (Sandbox Code Playgroud)

否则使用foo = "$fooWorld"将无法正常工作.


jca*_*llo 8

如果您要做的是将字符串拆分为多行,则可以使用反斜杠:

$ a="hello\
> world"
$ echo $a
helloworld
Run Code Online (Sandbox Code Playgroud)

中间有一个空格:

$ a="hello \
> world"
$ echo $a
hello world
Run Code Online (Sandbox Code Playgroud)

这个也只增加了一个空格:

$ a="hello \
>      world"
$ echo $a
hello world
Run Code Online (Sandbox Code Playgroud)


Boh*_*dan 7

更安全的方式:

a="AAAAAAAAAAAA"
b="BBBBBBBBBBBB"
c="CCCCCCCCCCCC"
d="DD DD"
s="${a}${b}${c}${d}"
echo "$s"
AAAAAAAAAAAABBBBBBBBBBBBCCCCCCCCCCCCDD DD
Run Code Online (Sandbox Code Playgroud)

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

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


dio*_*ovk 7

有一个特殊情况需要注意:

user=daniel
cat > output.file << EOF
"$user"san
EOF
Run Code Online (Sandbox Code Playgroud)

会输出"daniel"san,而不是danielsan你可能想要的输出.在这种情况下,你应该做:

user=daniel
cat > output.file << EOF
${user}san
EOF
Run Code Online (Sandbox Code Playgroud)


小智 7

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

foo='Hello'
foo=$foo' World'
echo $foo
Run Code Online (Sandbox Code Playgroud)

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


小智 6

a="Hello,"
a=$a" World!"
echo $a
Run Code Online (Sandbox Code Playgroud)

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


nop*_*ole 5

如果它是添加" World"到原始字符串的示例,那么它可以是:

#!/bin/bash

foo="Hello"
foo=$foo" World"
echo $foo
Run Code Online (Sandbox Code Playgroud)

输出:

Hello World
Run Code Online (Sandbox Code Playgroud)


小智 5

var1='hello'
var2='world'
var3=$var1" "$var2 
echo $var3
Run Code Online (Sandbox Code Playgroud)

  • `var3 = $ var1\$ var2`也有同样的效果 (2认同)

Bru*_*sky 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
Run Code Online (Sandbox Code Playgroud)

测试 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
Run Code Online (Sandbox Code Playgroud)

测试 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
Run Code Online (Sandbox Code Playgroud)

错误表明我的 Bash在崩溃之前达到了335.54432 MB。您可以将代码从将数据加倍更改为附加常量以获得更细粒度的图形和故障点。但我认为这应该给你足够的信息来决定你是否关心。就个人而言,低于 100 MB 我没有。你的旅费可能会改变。


sim*_*bac 5

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

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

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

echo ${string}
Run Code Online (Sandbox Code Playgroud)

然后我得到以下输出:

1 2 3 4 5
Run Code Online (Sandbox Code Playgroud)