在shell脚本中替换另一个字符串的子字符串

Zin*_*ode 701 bash shell

我有"我爱Suzi和Marry",我想把"Suzi"改为"Sara".

#!/bin/bash
firstString="I love Suzi and Marry"
secondString="Sara"
# do something...
Run Code Online (Sandbox Code Playgroud)

结果必须是这样的:

firstString="I love Sara and Marry"
Run Code Online (Sandbox Code Playgroud)

rua*_*akh 1155

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

#!/bin/bash
firstString="I love Suzi and Marry"
secondString="Sara"
echo "${firstString/Suzi/$secondString}"    
# prints 'I love Sara and Marry'
Run Code Online (Sandbox Code Playgroud)

要替换所有实例,请使用:${parameter//pattern/string}

message='The secret code is 12345'
echo "${message//[0-9]/X}"           
# prints 'The secret code is XXXXX'
Run Code Online (Sandbox Code Playgroud)

(这是在记录所述击参考手册,§3.5.3"壳牌参数扩展".)

请注意,POSIX没有指定此功能 - 它是Bash扩展 - 因此并非所有Unix shell都实现它.有关POSIX文档,请参阅Open Group技术标准基本规范,第7期,Shell&Utilities卷,第2.6.2节"参数扩展".

  • 要清楚,因为这让我感到困惑,第一部分必须是变量引用.你不能做`echo $ {my string foo/foo/bar}`.你需要`input ="my string foo"; echo $ {input/foo/bar}` (15认同)
  • 这是否可以将“\n”(换行符)替换为“<br />”(html 中断)?`$STRING="${STRING/\n/<br />}"` (5认同)
  • @ruakh如何用a或condition写这个语句.如果我想用新的字符串替换Suzi或Marry. (3认同)
  • @ Priyatham51:没有内置功能.只需更换一个,然后另一个. (3认同)
  • @Bu:不,因为在该上下文中的`\n`代表自己,而不是换行符.我现在没有Bash方便测试,但你应该能写出类似`$ STRING ="$ {STRING/$'\n'/ <br />}"`的东西.(虽然你可能想要`STRING //` - replace-all - 而不只是`STRING /`.) (3认同)
  • @mgutt:这个答案链接到相关文档。该文档并不完美 - 例如,它没有直接提及 `//`,而是提到了发生的情况“如果 *pattern* 以 '`/`' 开头”(这甚至不准确,因为该句子的其余部分假设额外的“/”实际上不是模式的一部分)——但我不知道有任何更好的来源。 (2认同)

Kev*_*vin 178

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

first="I love Suzy and Mary"
second="Sara"
first=${first/Suzy/$second}
Run Code Online (Sandbox Code Playgroud)

这将只取代第一次出现; 要替换它们,将第一个斜线加倍:

first="Suzy, Suzy, Suzy"
second="Sara"
first=${first//Suzy/$second}
# first is now "Sara, Sara, Sara"
Run Code Online (Sandbox Code Playgroud)

  • 如果 `first` 或 `second` 包含特殊字符,例如 `/`、`$`、`{`、`}`、&lt;反斜杠&gt;、`.`、`+`、`(`、`)`、 `*` 等?(此评论中反斜杠的格式问题。)也许可以在答案中解决这个问题? (17认同)
  • 有一个答案可以复制已接受的答案,而不是使用全局替换选项编辑已接受的答案,这有什么意义呢?并且支持添加regexp,同时支持它.[和](https://stackoverflow.com/posts/13210909/revisions)解释了怎么了"糟糕的替代".你有足够的积分,@凯文:) (7认同)
  • 看来他们都在同一分钟内回答了:O (2认同)

Ken*_*ent 61

试试这个:

 sed "s/Suzi/$secondString/g" <<<"$firstString"
Run Code Online (Sandbox Code Playgroud)

  • 你实际上并不需要Sed; Bash原生支持这种替换. (18认同)
  • 我想这是标记为"bash"但是来到这里因为需要一些简单的另一个shell.这是一个很好的简洁替代https://wiki.ubuntu.com/DashAsBinSh#A.24.7Bparm.2BAC8.3F.2BAC8-pat.5B.2BAC8-str.5D.7D使它看起来像我需要的. (11认同)
  • @NamGVU,或任何其他人收到该错误,这通常意味着您的搜索或替换部分中的字符串之一与您的分隔符具有相同的字符。例如,通常使用 / 作为分隔符,但如果您的字符串包含 *nix 目录,那么您会看到该错误,因为它们也具有 / 字符。 (4认同)
  • 这适用于灰/短划线或任何其他POSIX sh. (3认同)
  • 我收到错误 **sed: -e expression #1, char 9: unknown option to \`s** (2认同)
  • 第一个与标准输入一起使用的答案,非常适合管道字符串。 (2认同)
  • @ruakh 这个答案适用于**管道**。就像`my_command | sed ... | ...` (2认同)

Rom*_*kyi 59

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

POSIX sh兼容解决方案是:

result=$(echo "$firstString" | sed "s/Suzi/$secondString/")
Run Code Online (Sandbox Code Playgroud)

  • @Qstnr_La 使用双引号进行变量替换:`result=$(echo $firstString | sed "s/Suzi/$secondString/g")` (3认同)
  • 我得到这个: _$ echo $(echo $firstString | sed 's/Suzi/$secondString/g')_ **我爱 $secondString 和 Marry** (2认同)
  • 加 1 用于显示如何输出到变量。谢谢! (2认同)
  • 我固定了单引号,并在`echo`参数周围添加了缺失的引号。它看似不使用简单的字符串引用就可以工作,但是很容易在任何不重要的输入字符串(不规则间距,shell元字符等)上中断。 (2认同)

Cam*_*tin 45

使用bash比使用sedRegExp字符更好.

echo ${first_string/Suzi/$second_string}
Run Code Online (Sandbox Code Playgroud)

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

为了表明你不需要担心逃避让我们转过来:

/home/name/foo/bar
Run Code Online (Sandbox Code Playgroud)

进入:

~/foo/bar
Run Code Online (Sandbox Code Playgroud)

但只有/home/name在开始的时候.我们不需要sed!

鉴于庆典带给我们神奇的变量$PWD$HOME,我们可以:

echo "${PWD/#$HOME/\~}"
Run Code Online (Sandbox Code Playgroud)

编辑:感谢Mark Haferkamp在关于引用/转义的说明的评论中~.*

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

进一步阅读:高级Bash-Scripting Guide.
如果sed必须使用,请务必逃避每个角色.

  • @MarkHaferkamp Bash 及其晦涩的陷阱......:P (3认同)
  • @MarkHaferkamp [请参阅此](http://tldp.org/LDP/abs/html/string-manipulation.html#SUBSTRREPL02) 来自“推荐进一步阅读”链接。关于“转义‘~’”:注意我是如何引用的。记住总是引用东西!这不仅适用于魔法变量:任何变量都能够在 bash 中进行替换、获取字符串长度等。恭喜您尝试快速使用“$PS1”:如果您更熟悉,您可能还会对 [`$PROMPT_COMMAND`](http://tldp.org/HOWTO/Bash-Prompt-HOWTO/x264.html) 感兴趣另一种编程语言,并且想要编写编译提示符。 (2认同)

Jos*_*das 19

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

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

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


Alb*_*lla 16

echo [string] | sed "s/[original]/[target]/g"
Run Code Online (Sandbox Code Playgroud)
  • “s”的意思是“替代”
  • “g”的意思是“全局的,所有匹配的出现”

  • “g”标志被严重误解了。如果没有它,“sed”将替换*每行*上的第一次出现,但如果您不希望每行出现多次,则不需要“g”。(经常你会在每行只能有一个匹配的表达式中看到它,比如`s/.*everything.*/all of it/g`,显然你首先匹配整行,所以有您不可能多次匹配正则表达式)。 (4认同)
  • 当用“/”替换路径时,这非常有效 (2认同)

Pay*_*yam 9

我知道这是旧的,但因为没有人提到使用 awk:

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

  • 如果您尝试拆分的内容实际上不在变量中而是在文本文件中,则可以使用此技巧。谢谢,@Payam! (3认同)

agc*_*agc 8

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

输出:

I love Sara and Marry
Run Code Online (Sandbox Code Playgroud)

这个怎么运作:

  • 删除最小后缀模式。 如果后缀“ ”在“ ”中,则"${f%$t*}"返回“ ”。 I love$tSuzi*$fI love Suzi and Marry

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

  • 如果这是用来测试$t是在$f[ "${f%$t*}" != "$f" ]其中将评估为如果$f字符串包含“ Suzi*”的虚假如果不是。

  • 如果测试返回true,则使用移除最小后缀模式 ${f%$t*}I love”和移除最小前缀模式 ${f#*$t}and Marry”构建所需的字符串,中间有第二个字符串$sSara”。


Tow*_*hin 8

用特殊章程替换第一次出现的模式:

${参数/模式/字符串}

用特殊章程替换所有出现的模式:

${参数//模式/字符串}

firstString="I love //Suzi// and Marry"
secondString="Sara"
firstString="${firstString/\/\/Suzi\/\//"$secondString"}"
echo $firstString
Run Code Online (Sandbox Code Playgroud)

它将打印:I love Sara and Marry


Tom*_*Gal 8

我认为这是适合您的用例的最干净的形式:

firstString="${firstString//Suzi/$secondString}"
Run Code Online (Sandbox Code Playgroud)


小智 7

由于我无法添加评论。@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}
Run Code Online (Sandbox Code Playgroud)

  • 在我看到你的例子之前,我已经理解了相反的顺序。这有助于澄清正在发生的事情 (3认同)