ChatGPT解决这个技术问题 Extra ChatGPT

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

我有“我爱 Suzi 和 Marry”,我想把“Suzi”改成“Sara”。

firstString="I love Suzi and Marry"
secondString="Sara"

期望的结果:

firstString="I love Sara and Marry"
郑重声明,从前这可能是一个很好的问题,但多年来,Stack Overflow 不鼓励“给我代码”类型的问题。请不要将此作为如何在这里提问的好例子。
顺便说一句,Suzi、Marry 和 Sara 是谁?
@tripleee 为什么不呢?我发现它很有用
@Neil General“请给我代码”没有研究工作的问题是有问题的,因为除其他外,我们无法猜测 OP 的基线。无论如何,即使在 2012 年(甚至早在 1997 年),网络上也充满了“如何用另一个字符串替换一个字符串”的问题。这个网站现在在许多搜索引擎中排名很高,所以这个问题现在正在抢夺一些访问者信息量更大的页面。

o
oguz ismail

要使用给定字符串替换模式的 first 出现,请使用 ${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'

(这在 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:没有内置功能。只需更换一个,然后另一个。
这是否适用于用“<br />”替换“\n”(换行符) (html中断)? $STRING="${STRING/\n/<br />}"
@Bu:不,因为在这种情况下 \n 将代表它自己,而不是换行符。我现在没有手边的 Bash 来测试,但您应该可以编写类似 $STRING="${STRING/$'\n'/<br />}" 的内容。 (尽管您可能想要 STRING// -- replace-all -- 而不仅仅是 STRING/。)
需要明确的是,因为这让我有点困惑,所以第一部分必须是一个变量引用。你不能做echo ${my string foo/foo/bar}。你需要 input="my string foo"; echo ${input/foo/bar}
K
Kevin

这可以完全通过 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
如果 firstsecond 包含特殊字符,例如 /${}、<反斜杠>、.+(),该怎么办? ,*等? (此评论中反斜杠格式的问题。)也许在答案中解决这个问题?
t
tripleee

对于 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 和 Marry
@Qstnr_La 使用双引号进行变量替换:result=$(echo $firstString | sed "s/Suzi/$secondString/g")
加 1 也用于显示如何输出到变量。谢谢!
我修复了单引号并在 echo 参数周围添加了缺少的引号。它在不使用简单字符串引用的情况下具有欺骗性地工作,但很容易在任何重要的输入字符串(不规则间距、shell 元字符等)上中断。
在 sh (AWS Codebuild / Ubuntu sh)中,我发现最后需要一个单斜杠,而不是双斜杠。我将编辑评论,因为上面的评论也显示了一个斜线。
P
Peter Mortensen

尝试这个:

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

您实际上并不需要 Sed。 Bash 本身就支持这种替换。
我想这被标记为“bash”,但来到这里是因为需要另一个外壳简单的东西。这是 wiki.ubuntu.com/… 使它看起来像我需要的一个很好的简洁替代方案。
这适用于 ash/dash 或任何其他 POSIX sh。
我收到错误 sed: -e expression #1, char 9: unknown option to `s
第一个适用于标准输入的答案,非常适合流水线化字符串。
P
Peter Mortensen

如果字符串具有正则表达式字符,则使用 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/\~}"

感谢 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 上运行。您能否更新您的帖子以引用或转义 ~ 以获得更好的兼容性?我的“魔术变量”问题的意思是:我可以在例如 uname 的输出上使用 Bash 的变量替换而不先将其保存为变量吗?至于我个人的$PROMPT_COMMANDit's complicated
@MarkHaferkamp 哇,你完全正确,我的错。现在将更新答案。
@MarkHaferkamp Bash 及其隐蔽的陷阱......:P
P
Peter Mortensen

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

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

必须有 50 ways to leave your lover


A
Alberto Salvia Novella
echo [string] | sed "s/[original]/[target]/g"

“s”的意思是“替代”

“g”表示“全局,所有匹配的事件”


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

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

在我来到你的例子之前,我已经理解了相反的顺序。这有助于澄清正在发生的事情
t
tripleee

使用 AWK

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

如果您要拆分的内容实际上不是在变量中而是在文本文件中,那么该技巧就是要走的路。谢谢,@Payam!
a
agc

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 Suzi and Marry" 中,"${f%$t*}" 返回 "I love"。

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

这用于测试 $t 是否在带有 [ "${f%$t*}" != "$f" ] 的 $f 中,如果 $f 字符串包含 "Suzi*" 则评估为 true,否则为 false .

如果测试返回 true,则使用 Remove Smallest Suffix Pattern ${f%$t*} "I love" 和 Remove Smallest Prefix Pattern ${f#*$t} "and Marry" 构造所需的字符串,并使用第二个字符串 $s “萨拉”介于两者之间。


值得注意的是,这取决于 t 在 f 中仅出现一次,但可以通过循环和一个最长匹配来修改它以替换多次出现
C
CodeKiller

我发现的唯一方法是将字符串存储在文件中,使用 sed 然后将文件内容存储在 var 中:

echo "I love Suzy" > tmp.txt
sed -i "s/Suzy/Sarah/" tmp.txt
set res=`cat tmp.txt`
echo $res
rm tmp.txt

我不知道我使用的是哪种 shell(如果我键入“sh”,我发现只有 sh-4.2),但所有经典语法都失败了,比如简单的 test=${test2}。它失败了 2 次:分配时(必须使用 set)和 ${}