sed 用管道和星号替换字符串

Posted

技术标签:

【中文标题】sed 用管道和星号替换字符串【英文标题】:sed replace string with pipe and stars 【发布时间】:2021-03-15 20:00:49 【问题描述】:

我有以下字符串:

|**barak**.version|2001.0132012031539|

在文件text.txt 中。 我想用以下内容替换它:

|**barak**.version|2001.01.2012031541|

所以我跑了:

sed -i "s/\|\*\*$module\*\*.version\|2001.0132012031539/|**$module**.version|$version/" text.txt

但结果是重复而不是替换:

|**barak**.version|2001.01.2012031541|**barak**.version|2001.0132012031539|

我做错了什么? 这是模块和版本的值:

$ echo $module
barak
$ echo $version
2001.01.2012031541

【问题讨论】:

在知道您的脚本生成正确的输出之前,不要将-i 选项与sed 一起使用。在您知道结果(可能)正确之前覆盖您的文件是很疯狂的。 @Cyrus,感谢您的编辑。 @Jonathan Leffler - 小命令蚂蚁它返回到上一个:$ echo $test > text.txt 坏习惯会在你最不希望它们发生的时候伤害它们,当你最不能承受它们的时候。如果您重视自己的理智,请不要在未调试的sed 脚本上使用-i。 (这通常不会让你失去工作,但如果你不小心的话,它可能会达到那个水平。) 无法重现:当我复制'n'粘贴您的sed 脚本并将-i 替换为-e(删除它也可以)时,我从给定的输入中得到了预期的输出。 module='barak'version='2001.01.2012031541'echo '|**barak**.version|2001.0132012031539|' |sed -e "s/\|\*\*$module\*\*.version\|2001.0132012031539/|**$module**.version|$version/" — 代码块中的长破折号代表换行符。 【参考方案1】:

假设:

感兴趣的行以管道 (|) 开始和结束,并且在数据中间的某处还有一个管道 搜索仅基于数据中第一个/第二个管道之间存在的$module 的值 我们不知道第一个/第二个管道之间还有什么 版本号是 2nd/3rd 管道之间唯一的东西 我们不知道要替换的版本号

样本数据:

$ module='barak'
$ version='2001.01.2012031541'

$ cat text.txt
 **barak**.version|2001.0132012031539|     <<<=== leave this one alone
|**apple**.version|2001.0132012031539|
|**barak**.version|2001.0132012031539|     <<<=== replace this one
|**chuck**.version|2001.0132012031539|
|**barak**.peanuts|2001.0132012031539|     <<<=== replace this one

一个sed 解决方案启用-Extended 正则表达式支持并使用捕获组:

$ sed -E "s/^(\|[^|]*$module[^|]*).*/\1|$version|/" text.txt

地点:

\| - 第一次出现(转义管道)告诉 sed 我们正在处理文字管道;后续管道将被视为文字字符串 ^(\|[^|]*$module[^|]*) - 从行首开始的第一个捕获组,以竖线开头,然后是一些非竖线字符,然后是搜索模式 ($module),然后是更多非竖线字符(持续到下一个管道字符) .* - 匹配行的其余部分(我们将丢弃) \1|$version| - 用我们的第一个捕获组替换行,然后是管道,然后是新的替换值 ($version),然后是最终管道

以上生成:

 **barak**.version|2001.0132012031539|
|**apple**.version|2001.0132012031539|
|**barak**.version|2001.01.2012031541|     <<<=== replaced
|**chuck**.version|2001.0132012031539|
|**barak**.peanuts|2001.01.2012031541|     <<<=== replaced

【讨论】:

像魅力一样工作!谢谢!顺便说一句 - 在我的情况下不需要 /g - 只需要替换一次。【参考方案2】:

使用 GNU awk 的 awk 替代方案:

awk -v mod="$module" -v vers="$version" -F \| ' OFS=FS;split($2,map,".");inmod=substr(map[1],3,length(map[1])-4);if (inmod==mod)  $3=vers  1' file

使用 $module 和 $version 将两个变量 mod 和 vers 传递给 awk。将字段分隔符设置为 |。使用 split 函数和使用 . 将第二个字段拆分为数组映射。作为分隔符。然后从数组的第一个索引中去除开头和结尾的“**”,以使用 substr 函数将模块名称公开为 inmod。将此与 mod 变量进行比较,如果匹配,则将第三个分隔字段更改为变量 vers。用简写方式打印行 1

【讨论】:

【参考方案3】:

管道仅在您使用 扩展 正则表达式时是特殊的:sed -E 没有理由在这里需要扩展,坚持使用基本的正则表达式:

sed "
    # for lines matching module.version
    /|\*\*$module\*\*.version|/ 
        # replace the version
        s/|2001.0132012031539|/|$version|/
    
" text.txt

或作为不可读的单行字

sed "/|\*\*$module\*\*.version|/ s/|2001.0132012031539|/|$version|/" text.txt

【讨论】:

我厌倦了你的建议,它似乎打印了相同的字符串:sed "/|\*\*$module\*\*.version|/ s/|2001.0132012031539|/|$versionX|/" text.txt |**barak**.version|2001.01.2012031539| BTW 为什么我们需要大写 X? X 是错字 我在没有 X 的情况下也尝试过,但结果还是得到了相同的输入

以上是关于sed 用管道和星号替换字符串的主要内容,如果未能解决你的问题,请参考以下文章

linux 特殊符号怎样用sed替换

使用 GSUTIL 替换字符串

linux 给文件替换字符串/替换内容/替换某行 (shell,sed)

手机号码中间部分替换成星号

sed 用特殊字符替换字符串

用sed替换一行全局字符串