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

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

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

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

这在Bash中是如何实现的?

4671192 次浏览
foo="Hello"foo="${foo} World"echo "${foo}"> Hello World

一般来说,要连接两个变量,您可以一个接一个地编写它们:

a='Hello'b='World'c="${a} ${b}"echo "${c}"> Hello World
$ a=hip$ b=hop$ ab=$a$b$ echo $abhiphop$ echo $a$bhiphop
foo="Hello "foo="$foo World"

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

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

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

这是一个Bash脚本的示例:

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

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

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

输出

x y z

你也可以这样做:

$ var="myscript"
$ echo $var
myscript

$ var=${var}.sh
$ echo $var
myscript.sh

如果您要做的是将字符串分裂分成几行,您可以使用反斜杠:

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

中间有一个空格:

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

这也只在两者之间增加了一个空格:

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

另一种方法…

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

…还有一个。

> H="Hello "> U=$H"universe."> echo $UHello universe.
bla=hellolaber=kthxecho "${bla}ohai${laber}bye"

将输出

helloohaikthxbye

这是有用的$blaohai导致变量未找到错误。或者如果您的字符串中有空格或其他特殊字符。"${foo}"正确地转义您放入的任何内容。

Bash先来

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

+=:附加到变量

语法#0可以以不同的方式使用:

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

(因为我很节俭,所以我只使用两个变量fooa,然后在整个答案中重复使用相同的变量。

a=2a+=4echo $a24

使用堆栈溢出问题语法,

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

工作正常!

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

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

echo $a24((a+=12))echo $a36

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

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

echo ${a[@]}36
a+=(18)
echo ${a[@]}36 18echo ${a[0]}36echo ${a[1]}18

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

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

嗯…这不是bug,而是一个特色…要防止bash尝试开发!",您可以:

a+=(one word "hello world"! 'hello world!' $'hello world\041')
declare -p adeclare -a a='([0]="36" [1]="18" [2]="one" [3]="word" [4]="hello world!" [5]="hello 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 $a36./.18...'one' 'word', 'hello world!'=='hello world!'=='hello world!'

或者我们可以使用单参数格式字符串,它将随着提交的参数的数量而重复…

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

declare -p adeclare -a a='([0]="36./.18...'\''one'\'' '\''word'\'', '\''hello world!'\''=='\''hello world!'\''=='\''hello world!'\''" [1]="18" [2]="one" [3]="word" [4]="hello world!" [5]="hello world!" [6]="hello world!")'

在bash下,当你访问一个变量名而不指定索引时,你总是只处理第一个元素!

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

a=36declare -p adeclare -a a='([0]="36" [1]="18" [2]="one" [3]="word" [4]="hello world!" [5]="hello 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" $fooecho $fooHello World

注意:使用双引号可能对操作包含spacestabulations和/或newlines的字符串有用

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

壳牌现在

POSIX shell下,你不能使用巴什主义,所以没有内置printf

基本上

你可以简单地做:

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

格式化,使用分叉printf

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

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

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

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

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

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

使用反引号,您必须使用反斜杠来逃避内部分叉:

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

如果您想附加下划线之类的内容,请使用转义(\)

FILEPATH=/opt/myfile

没有工作:

echo $FILEPATH_$DATEX

这工作得很好:

echo $FILEPATH\\_$DATEX

注意这是行不通的

foo=HELLObar=WORLDfoobar=PREFIX_$foo_$bar

因为它似乎删除了$foo并留给你:

PREFIX_WORLD

但这将工作:

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

并为您留下正确的输出:

PREFIX_HELLO_WORLD

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

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

更安全的方法:

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

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

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

我解决问题的方法就是

$a$b

例如,

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

其产生

Hello World

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

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

那么echo "$c"将产生

Hello World

一个额外的空间。

$aWorld

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

${a}World

产生

HelloWorld

这是一个通过AWK

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

我喜欢做一个快速的功能。

#! /bin/sh -ffunction combo() {echo $@}
echo $(combo 'foo''bar')

又一种给猫剥皮的方法。这次是带函数的:D

即使现在允许+=运算符,它也是在2004年的bash3.1中引入的。

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

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

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

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

#!/bin/bash
foo="Hello"foo=$foo" World"echo $foo

输出:

Hello World

有一个特殊的情况下,你应该注意:

user=danielcat > output.file << EOF"$user"sanEOF

将输出"daniel"san,而不是danielsan,正如您可能想要的那样。在这种情况下,你应该这样做:

user=danielcat > output.file << EOF${user}sanEOF

使用引号的最简单方法:

B=Barb=barvar="$B""$b""a"echo "Hello ""$var"
var1='hello'var2='world'var3=$var1" "$var2echo $var3

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

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

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

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

否则使用foo = "$fooWorld"将不起作用。

以下是大多数答案所谈论的内容的简明摘要。

假设我们有两个变量,1美元设置为“1”:

set one twoa=hellob=world

下表解释了我们可以组合ab的值以创建新变量c的不同上下文。

Context                               | Expression            | Result (value of c)--------------------------------------+-----------------------+---------------------Two variables                         | c=$a$b                | helloworldA variable and a literal              | c=${a}_world          | hello_worldA variable and a literal              | c=$1world             | oneworldA variable and a literal              | c=$a/world            | hello/worldA variable, a literal, with a space   | c=${a}" world"        | hello worldA more complex expression             | c="${a}_one|${b}_2"   | hello_one|world_2Using += operator (Bash 3.1 or later) | c=$a; c+=$b           | helloworldAppend literal with +=                | c=$a; c+=" world"     | hello world

一些注意事项:

  • 用双引号括住赋值的RHS通常是一种很好的做法,尽管在许多情况下它是可选的
  • 从性能的角度来看,如果以小增量构造大字符串,尤其是在循环中,+=更好
  • 在变量名周围使用{}来消除它们的扩展歧义(如上表中的第2行)。正如在第3行和第4行所看到的,不需要{},除非变量与以shell变量名中有效的第一个字符开头的字符串连接,即字母表或下划线。

另见:

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

(注意:macOS上的date不提供纳秒,因此必须在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 1Method: a="$a$a"1.               20 chars  time: 1513640431.861671143  delta: 1513640431.8616711432.               40 chars  time: 1513640431.865036344  delta: .0033652013.               80 chars  time: 1513640431.868200952  delta: .0031646084.              160 chars  time: 1513640431.871273553  delta: .0030726015.              320 chars  time: 1513640431.874358253  delta: .0030847006.              640 chars  time: 1513640431.877454625  delta: .0030963727.             1280 chars  time: 1513640431.880551786  delta: .0030971618.             2560 chars  time: 1513640431.883652169  delta: .0031003839.             5120 chars  time: 1513640431.886777451  delta: .00312528210.            10240 chars  time: 1513640431.890066444  delta: .00328899311.            20480 chars  time: 1513640431.893488326  delta: .00342188212.            40960 chars  time: 1513640431.897273327  delta: .00378500113.            81920 chars  time: 1513640431.901740563  delta: .00446723614.           163840 chars  time: 1513640431.907592388  delta: .00585182515.           327680 chars  time: 1513640431.916233664  delta: .00864127616.           655360 chars  time: 1513640431.930577599  delta: .01434393517.          1310720 chars  time: 1513640431.954343112  delta: .02376551318.          2621440 chars  time: 1513640431.999438581  delta: .04509546919.          5242880 chars  time: 1513640432.086792464  delta: .08735388320.         10485760 chars  time: 1513640432.278492932  delta: .19170046821.         20971520 chars  time: 1513640432.672274631  delta: .39378169922.         41943040 chars  time: 1513640433.456406517  delta: .78413188623.         83886080 chars  time: 1513640435.012385162  delta: 1.55597864524.        167772160 chars  time: 1513640438.103865613  delta: 3.09148045125.        335544320 chars  time: 1513640444.267009677  delta: 6.163144064./append_test.sh: fork: Cannot allocate memory

测试2:

$ ./append_test.sh 2Method: a+="$a"1.               20 chars  time: 1513640473.460480052  delta: 1513640473.4604800522.               40 chars  time: 1513640473.463738638  delta: .0032585863.               80 chars  time: 1513640473.466868613  delta: .0031299754.              160 chars  time: 1513640473.469948300  delta: .0030796875.              320 chars  time: 1513640473.473001255  delta: .0030529556.              640 chars  time: 1513640473.476086165  delta: .0030849107.             1280 chars  time: 1513640473.479196664  delta: .0031104998.             2560 chars  time: 1513640473.482355769  delta: .0031591059.             5120 chars  time: 1513640473.485495401  delta: .00313963210.            10240 chars  time: 1513640473.488655040  delta: .00315963911.            20480 chars  time: 1513640473.491946159  delta: .00329111912.            40960 chars  time: 1513640473.495354094  delta: .00340793513.            81920 chars  time: 1513640473.499138230  delta: .00378413614.           163840 chars  time: 1513640473.503646917  delta: .00450868715.           327680 chars  time: 1513640473.509647651  delta: .00600073416.           655360 chars  time: 1513640473.518517787  delta: .00887013617.          1310720 chars  time: 1513640473.533228130  delta: .01471034318.          2621440 chars  time: 1513640473.560111613  delta: .02688348319.          5242880 chars  time: 1513640473.606959569  delta: .04684795620.         10485760 chars  time: 1513640473.699051712  delta: .09209214321.         20971520 chars  time: 1513640473.898097661  delta: .19904594922.         41943040 chars  time: 1513640474.299620758  delta: .40152309723.         83886080 chars  time: 1513640475.092311556  delta: .79269079824.        167772160 chars  time: 1513640476.660698221  delta: 1.56838666525.        335544320 chars  time: 1513640479.776806227  delta: 3.116108006./append_test.sh: fork: Cannot allocate memory

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

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

list=(1 2 3 4 5)string=''
for elm in "${list[@]}"; dostring="${string} ${elm}"done
echo ${string}

然后我得到以下输出:

1 2 3 4 5
a="Hello,"a=$a" World!"echo $a

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

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

function concat (){prefix=$1suffix=$2
echo "${prefix}${suffix}"}
foo="Super"bar="man"
concat $foo $bar   # Superman
alien=$(concat $foo $bar)
echo $alien        # Superman

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

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

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

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

Bash算术接受ASCII/字符串数字作为输入,因此实际使用整数属性的原因很少。

此外,变量值不能包含ASCIINULL(即8位零),因为常规的以空结尾的C字符串用于实现它们。

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

附加一个字符串:

$ foo=Hello$ foo+=' world!'$ echo "$foo"Hello world!
$ num=3$ num+=4echo "$num"34 # Appended string (not a sum)

使用integer属性的几个原因之一是它改变了+=赋值操作符的行为:

$ declare -i num=3$ num+=4echo "$num"7 # Sum

请注意,这对-=/=等不起作用,除非你在算术((( ))$(( )))中这样做,其中数字已经在带有或不带有整数属性的情况下被视为相同。有关这些运算符的完整列表,请参阅man bash的“算术评估”部分,它们与C相同。

#0赋值操作符也可用于将新元素附加到索引数组(又名“列表”):

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

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

$ foo[c++]=one$ foo[c++]=two

POSIX shell做not使用#0赋值操作符来追加字符串,所以你必须这样做:

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

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