关于bash:在shell脚本中替换另一个字符串的一个子字符串

Replace one substring for another string in shell script

我有"我爱苏西并结婚",我想把"苏西"改成"萨拉"。

1
2
3
4
#!/bin/bash
firstString="I love Suzi and Marry"
secondString="Sara"
# do something...

结果必须如下:

1
firstString="I love Sara and Marry"

要用给定的字符串替换模式的第一个匹配项,请使用${parameter/pattern/string}

1
2
3
4
5
#!/bin/bash
firstString="I love Suzi and Marry"
secondString="Sara"
echo"${firstString/Suzi/$secondString}"    
# prints 'I love Sara and Marry'

要替换所有事件,请使用${parameter//pattern/string}

1
2
3
message='The secret code is 12345'
echo"${message//[0-9]/X}"          
# prints 'The secret code is XXXXX'

(这记录在bash参考手册和第3.5.3节"shell参数扩展"中。)

请注意,这个特性不是由posix指定的,它是bash扩展,因此并非所有的unix shell都实现它。有关POSIX的相关文档,请参阅OpenGroup技术标准基本规范,第7期,Shell&utilities卷,以及第2.6.2节"参数扩展"。


这完全可以通过bash字符串操作完成:

1
2
3
first="I love Suzy and Mary"
second="Sara"
first=${first/Suzy/$second}

这将只替换第一个匹配项;要全部替换,请将第一个斜杠加倍:

1
2
3
4
first="Suzy, Suzy, Suzy"
second="Sara"
first=${first//Suzy/$second}
# first is now"Sara, Sara, Sara"


试试这个:

1
 sed"s/Suzi/$secondString/g" <<<"$firstString"


如果字符串有regexp字符,那么使用bash比使用sed要好。

1
echo ${first_string/Suzi/$second_string}

它可移植到Windows中,至少与bash 3.1一样旧。

为了证明你不需要太担心逃跑,让我们来看看:

1
/home/name/foo/bar

进入这个:

1
~/foo/bar

但前提是/home/name是在开始的时候。我们不需要sed

考虑到bash给了我们神奇的变量$PWD$HOME,我们可以:

1
echo"${PWD/#$HOME/\~}"

编辑:感谢Mark Haferkamp对引用/转义~注释的评论。*

注意变量$HOME如何包含斜线,但这并没有破坏任何东西。

进一步阅读:高级bash脚本指南。如果必须使用sed,请确保转义每个字符。


对于dash,所有以前的帖子都不起作用

posix sh兼容的解决方案是:

1
result=$(echo"$firstString" | sed"s/Suzi/$secondString/")


如果明天你决定不爱结婚,她也可以被取代:

1
2
3
today=$(</tmp/lovers.txt)
tomorrow="${today//Suzi/Sara}"
echo"${tomorrow//Marry/Jesica}"> /tmp/lovers.txt

必须有50种方法离开你的爱人。


纯posix shell方法与基于Roman Kazanovskyi的sed的答案不同,它不需要外部工具,只需要shell自己的本地参数扩展。请注意,长文件名被最小化,因此代码更适合一行:

1
2
3
4
5
f="I love Suzi and Marry"
s=Sara
t=Suzi
["${f%$t*}" !="$f" ] && f="${f%$t*}$s${f#*$t}"
echo"$f"

输出:

1
I love Sara and Marry

它是如何工作的:

  • 删除最小后缀模式。如果后缀$t中的"Suzi*$f中,I loveSuzi and Marry中,"${f%$t*}"返回"EDOCX1〔13]。

  • 但如果t=Zelda,则"${f%$t*}"不删除任何内容,并返回整个字符串"I love Suzi and Marry"。

  • 用于测试$t是否在$f中,["${f%$t*}" !="$f" ]是否在$f中,如果$f字符串包含"Suzi*则评估为真,否则评估为假。

  • 如果测试返回true,则使用删除最小后缀模式${f%$t*}"I love"构造所需字符串,并删除最小前缀模式${f#*$t}"and Marry",其中第二个字符串$s"Sara"。


因为我不能添加评论。@Ruaka为了使示例更易读,请这样写

1
2
3
4
5
6
full_string="I love Suzy and Mary"
search_string="Suzy"
replace_string="Sara"
my_string=${full_string/$search_string/$replace_string}
or
my_string=${full_string/Suzy/Sarah}