将一个子字符串替换为Shell脚本中的另一个字符串


820

我有“我爱Suzi和结婚”,我想将“ Suzi”更改为“ Sara”。

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

结果必须是这样的:

firstString="I love Sara and Marry"

15
首先,让Suzi轻轻放下。:)
codeniffer

不是你而是我 !应该是与Suzi交谈的字符串
GoodSp33d

Answers:


1357

要用给定的字符串替换第一次出现的模式,请使用:${parameter/pattern/string}

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

要替换所有出现的情况,请使用:${parameter//pattern/string}

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文档,请参见《 Open Group技术标准基本规范》第7期Shell&Utilities卷,第2.6.2节“参数扩展”


3
@ruakh我如何用or条件编写此语句。即使我想用新的字符串替换Suzi或Marry。
Priyatham51 2014年

3
@ Priyatham51:没有内置功能。只需更换一个,然后更换另一个。
ruakh 2014年

4
@Bu:不,因为\n在这种情况下它将代表自身,而不是换行符。我现在没有方便的Bash进行测试,但是您应该可以编写类似的东西$STRING="${STRING/$'\n'/<br />}"。(尽管您可能想要STRING//-全部替换-而不是仅仅STRING/。)
ruakh 2015年

25
需要说明的是,由于这使我有些困惑,因此第一部分必须是变量引用。你做不到echo ${my string foo/foo/bar}。您需要input="my string foo"; echo ${input/foo/bar}
Henrik N

2
@mgutt:此答案链接到相关文档。该文档不是完美的-例如,它没有//直接提及,而是提及了“如果模式以' /'” 开头(甚至不准确,因为该句子的其余部分假定多余的/部分实际上不是该部分的一部分)模式)-但我不知道有任何更好的来源。
ruakh

208

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

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

那将仅替换第一次出现;要全部替换,请将第一个斜杠加倍:

first="Suzy, Suzy, Suzy"
second="Sara"
first=${first//Suzy/$second}
# first is now "Sara, Sara, Sara"

9
有一个答案可以复制接受的答案,而不用全局替换选项编辑接受的答案有什么意义?并添加了对正则表达式的支持。解释“不良替代”的问题。你有足够的分数,@ Kevin :)
Dan Dascalescu

6
看来他们都在同一分钟回答:O
Jamie Hutber '19

76

对于Dash,以前的所有帖子均无效

POSIX sh兼容的解决方案是:

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

1
我得到了:$ echo $(echo $ firstString | sed's / Suzi / $ secondString / g') 我爱$ secondString并嫁给
Qstnr_La

2
@Qstnr_La使用双引号代替变量:result=$(echo $firstString | sed "s/Suzi/$secondString/g")
emc

1
加号1用于显示如何输出到变量。谢谢!
法老王大厨

2
我固定了单引号,并在echo参数周围添加了缺少的引号。它看似不使用简单的字符串引用就可以工作,但是很容易在任何不平凡的输入字符串(不规则间距,shell元字符等)上中断。
三人房

在sh(AWS Codebuild / Ubuntu sh)中,我发现最后需要一个斜杠,而不是一个双斜杠。我将编辑注释,因为上面的注释也显示一个斜杠。
蒂姆(Tim)

67

尝试这个:

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

19
您实际上不需要Sed。Bash本机支持这种替换。
ruakh 2012年

12
我猜这被标记为“ bash”,但是来到这里是因为另一个外壳需要一些简单的东西。这是wiki.ubuntu.com/…的简洁替代,使它看起来像我所需要的。
natevw 2014年

3
这对于灰/破折号或任何其他POSIX sh非常有用。
Yoshua Wuyts 2015年

2
我收到错误sed:-e表达式#1,字符9:`s的未知选项
Nam G VU

1
与stdin一起使用的第一个答案,非常适合流水线字符串。
Dinei

46

sed字符串具有RegExp字符相比,使用bash更好。

echo ${first_string/Suzi/$second_string}

它可移植到Windows,并且至少与Bash 3.1一样老。

为了表明您不必担心转义,我们将其转为:

/home/name/foo/bar

变成这个:

~/foo/bar

但只有/home/name在开始时。我们不需要sed

鉴于bash为我们提供了神奇的变量$PWD$HOME,我们可以:

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

编辑:感谢马克·哈弗坎普(Mark Haferkamp)在引文中的注释~

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

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


这个答案使我无法使用sedpwd命令避免每次我的自定义$PS1运行时都定义一个新变量。Bash是否提供比魔术变量更通用的方式来使用命令的输出进行字符串替换?至于您的代码,为了避免~Bash将其扩展到$ HOME ,我不得不进行转义。另外,#您的命令中的作用是什么?
Mark Haferkamp,2015年

1
@MarkHaferkamp 看到这个从“进一步阅读建议”链接。关于“转义~”:注意我如何引用东西。记住总是引用东西!而且这不仅适用于魔术变量:在bash中,任何变量都能够替换,获取字符串长度等。恭喜您尝试$PS1快速:$PROMPT_COMMAND如果您对另一种编程语言更熟悉并且想编写一个编译后的提示,那么您可能也会感兴趣。
卡米洛·马丁

“更多阅读”链接说明“#”。在Bash 4.3.30上,echo "${PWD/#$HOME/~}"请勿将替换$HOME~~\~或代替'~'。这些都在另一个发行版上针对Bash 4.2.53进行。您能否更新您的帖子以引用或转义以~获得更好的兼容性?我的“魔术变量”问题是什么意思:我可以在例如Bash的输出上使用Bash的变量替换,uname而无需先将其保存为变量吗?至于我的个人$PROMPT_COMMAND这很复杂
Mark Haferkamp

@MarkHaferkamp哇,你说的很对,我很糟糕。现在将更新答案。
卡米洛·马丁

1
@MarkHaferkamp Bash及其晦涩的陷阱...:P
Camilo Martin,

19

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

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

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


9

由于我无法添加评论。@ruaka为了使示例更易读,请像这样编写

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}

直到我出现在您的示例中为止,我反过来才理解顺序。这有助于弄清正在发生的事情
raghav710

5

POSIX shell方法与Roman Kazanovskyised基于答案的方法不同,它不需要外部工具,而仅需要Shell自身的本机参数扩展。请注意,将长文件名最小化,因此代码更适合一行:

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

输出:

I love Sara and Marry

怎么运行的:

  • 删除最小的后缀图案。 如果后缀为“ ”,则"${f%$t*}"返回“ ”。 I love$tSuzi*$fI love Suzi and Marry

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

  • 这用于测试$tin $f,如果字符串包含“ ”,则结果[ "${f%$t*}" != "$f" ]true,否则为false$fSuzi*

  • 如果测试返回,使用构建体所需的字符串删除最小匹配模式 ${f%$t*}I love”和删除最小前缀模式 ${f#*$t}and Marry”,用第二$sSara在...之间”。


5

我知道这已经很老了,但是由于没有人提到使用awk:

    firstString="I love Suzi and Marry"
    echo $firstString | awk '{gsub("Suzi","Sara"); print}'

1
如果您要拆分的内容实际上不在变量中而是在文本文件中,那么该技巧就是解决之道。谢谢,@ Payam!
Felipe SS Schneider

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.