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

sed replace string with pipe and stars

我有以下字符串:

|**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

假设:

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

示例数据:

$ 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 regex 支持并使用捕获组:

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

其中:

  • \| - 第一次出现(转义管道)告诉 sed 我们正在处理文字管道;后续管道将被视为文字字符串
  • ^(\|[^|]*${module}[^|]*) - 第一个捕获组从行首开始,以竖线开始,然后是一些非竖线字符,然后是搜索模式 (${module}),然后更多非管道字符(继续到下一个管道字符)
  • .* - 匹配行的其余部分(我们将丢弃)
  • |${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

使用 GNU awk 的 awk 替代方案:

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

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

打印行

管道仅在您使用 扩展 正则表达式时才特殊: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