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
解决方案启用-E
xtended 正则表达式支持并使用捕获组:
$ 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 用管道和星号替换字符串的主要内容,如果未能解决你的问题,请参考以下文章