【问题标题】:Replace one substring for another string in shell script将一个子字符串替换为 shell 脚本中的另一个字符串
【发布时间】:2012-10-24 00:51:36
【问题描述】:

我有“I love Suzi and Marry”,我想把“Suzi”改成“Sara”。

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

结果一定是这样的:

firstString="I love Sara and Marry"

【问题讨论】:

  • 郑重声明,从前这可能是一个很好的问题,但多年来,Stack Overflow 确实鼓励“给我代码”类型的问题.请不要将此作为如何在这里提问的好例子。

标签: bash shell


【解决方案1】:

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

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

要替换所有次出现,请使用${<em>parameter</em>//<em>pattern</em>/<em>string</em>}

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

(这在the Bash Reference Manual, §3.5.3 "Shell Parameter Expansion"中有记录。)

请注意,POSIX 并未指定此功能——它是 Bash 扩展——因此并非所有 Unix shell 都实现它。有关相关 POSIX 文档,请参阅The Open Group Technical Standard Base Specifications, Issue 7, the Shell & Utilities volume, §2.6.2 "Parameter Expansion"

【讨论】:

  • @ruakh 如何用 or 条件编写此语句。就在我想用新字符串替换 Suzi 或 Marry 时。
  • @Priyatham51:没有内置功能。只需更换一个,然后再更换另一个。
  • @Bu:不,因为\n 在这种情况下会代表它自己,而不是换行符。我现在没有手边的 Bash 来测试,但你应该可以写类似 $STRING="${STRING/$'\n'/&lt;br /&gt;}" 的东西。 (尽管您可能想要 STRING// -- 全部替换 -- 而不仅仅是 STRING/。)
  • 要清楚,因为这让我有点困惑,所以第一部分必须是变量引用。你不能做echo ${my string foo/foo/bar}。你需要input="my string foo"; echo ${input/foo/bar}
  • @mgutt:此答案链接到相关文档。文档并不完美——例如,它没有直接提到//,而是提到了“如果pattern以'/'开头”会发生什么(这甚至不准确,因为这句话的其余部分假设额外的/ 实际上不是模式的一部分)——但我不知道有更好的来源。
【解决方案2】:

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

POSIX sh 兼容的解决方案是:

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

这将替换每行输入的第一个匹配项。添加/g 标志以替换所有出现的情况:

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

【讨论】:

  • 我得到了这个:$ echo $(echo $firstString | sed 's/Suzi/$secondString/g') 我爱 $secondString 和结婚 b>
  • @Qstnr_La 使用双引号进行变量替换:result=$(echo $firstString | sed "s/Suzi/$secondString/g")
  • 加 1 用于显示如何输出到变量。谢谢!
  • 我修复了单引号并在 echo 参数周围添加了缺少的引号。它在不引用简单字符串的情况下具有欺骗性地工作,但很容易在任何重要的输入字符串(不规则间距、shell 元字符等)上中断。
  • 在 sh (AWS Codebuild / Ubuntu sh) 中,我发现最后需要一个斜杠,而不是双斜杠。我要编辑评论,因为上面的 cmets 也显示一个斜线。
【解决方案3】:
echo [string] | sed "s/[original]/[target]/g"
  • “s”表示“替代”
  • “g”表示“全局,所有匹配的事件”

【讨论】:

  • g 标志被严重误解。没有它,sed 将替换 每行 的第一次出现,但如果您不希望每行出现多次,则不需要g。 (通常你会在每行只能匹配一个匹配的表达式中看到它,比如s/.*everything.*/all of it/g,显然你首先匹配的是整行,所以你不可能多次匹配正则表达式)。
【解决方案4】:

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

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

【讨论】:

  • 如果您要拆分的内容实际上不是在变量中而是在文本文件中,那么这个技巧就是要走的路。谢谢,@Payam!
【解决方案5】:

POSIX shell 方法,与Roman Kazanovskyi's sed-based answer 不同,它不需要外部工具,只需要shell 自己的原生parameter expansions。请注意,长文件名已最小化,因此代码更适合一行:

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

它是如何工作的:

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

  • 但是如果t=Zelda,那么"${f%$t*}"什么都不删除,并返回整个字符串“I love Suzi and Marry”。

  • 这用于测试$t 是否在$f[ "${f%$t*}" != "$f" ] 中,如果$f 字符串包含“Suzi*”,则评估结果为true " 和 false 如果不是。

  • 如果测试返回 true,则使用 Remove Smallest Suffix Pattern ${f%$t*} "I love" 构造所需的字符串和 删除最小前缀模式 ${f#*$t} "and Marry",使用 2nd 字符串 $s "Sara”介于两者之间。

【讨论】:

  • 值得注意的是,这取决于 t 在 f 中仅出现一次,但可以通过循环和最长匹配来修改它以替换多次出现
【解决方案6】:

因为我无法添加评论。 @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}

【讨论】:

  • 在我看到你的例子之前,我已经理解了相反的顺序。这有助于澄清正在发生的事情
【解决方案7】:

这可以完全通过 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"

【讨论】:

  • 看来他们都在同一分钟内回答了:O
【解决方案8】:

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

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

离开你的爱人,一定有50种方法。

【讨论】:

    【解决方案9】:

    如果字符串包含 RegExp 字符,则使用 bash 比使用 sed 更好。

    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/\~}"
    

    编辑:感谢 cmets 中的 Mark Haferkamp 提供关于引用/转义 ~.*

    的注释

    请注意变量 $HOME 是如何包含斜杠的,但这并没有破坏任何内容。

    进一步阅读:Advanced Bash-Scripting Guide.
    如果必须使用sed,请务必使用escape every character

    【讨论】:

    • 这个答案阻止我使用 sedpwd 命令来避免每次我的自定义 $PS1 运行时定义一个新变量。 Bash 是否提供了一种比魔术变量更通用的方法来使用命令的输出进行字符串替换?至于您的代码,我不得不转义 ~ 以防止 Bash 将其扩展到 $HOME。另外,命令中的# 有什么作用?
    • @MarkHaferkamp See this 来自“推荐的进一步阅读”链接。关于“转义~”:注意我是如何引用内容的。记住总是引用东西!这不仅适用于魔术变量:任何变量都能够在 bash 中进行替换、获取字符串长度等等。恭喜您尝试使用$PS1 快速:如果您更熟悉另一种编程语言并想要编写编译提示,您可能也对$PROMPT_COMMAND 感兴趣。
    • “进一步阅读”链接解释了“#”。在 Bash 4.3.30 上,echo "${PWD/#$HOME/~}" 不会用~ 替换我的$HOME。将~ 替换为\~'~' 有效。这些都可以在另一个发行版上的 Bash 4.2.53 上运行。您能否更新您的帖子以引用或转义 ~ 以获得更好的兼容性?我的“魔术变量”问题的意思是:我可以在不先将其保存为变量的情况下使用 Bash 的变量替换,例如 uname 的输出吗?至于我个人的$PROMPT_COMMANDit's complicated
    • @MarkHaferkamp 哇,你完全正确,我的错。现在将更新答案。
    • @MarkHaferkamp Bash 及其隐蔽的陷阱...:P
    【解决方案10】:

    试试这个:

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

    【讨论】:

    • 你实际上并不需要 Sed; Bash 本身就支持这种替换。
    • 我猜这被标记为“bash”,但来到这里是因为需要另一个 shell 的简单内容。这是 wiki.ubuntu.com/… 让它看起来像我需要的一个很好的简洁替代方案。
    • 这适用于 ash/dash 或任何其他 POSIX sh。
    • 我收到错误 sed: -e expression #1, char 9: unknown option to `s
    • 第一个适用于标准输入的答案,非常适合流水线字符串。
    猜你喜欢
    • 1970-01-01
    • 2011-04-06
    • 1970-01-01
    • 2012-07-16
    • 1970-01-01
    • 1970-01-01
    • 2017-03-28
    • 2023-03-22
    相关资源
    最近更新 更多