sed - 在第一个匹配行之后替换第一个出现的行

标签 sed

让我们假设以下 XML 文件:

    some text
    <addresses>
      <something/>
    </addresses>
    some more text
    <addresses xmlns="namespace">
      <could be anything/>
    </addresses>
    some other text
    <addresses>
      <something else/>
    </addresses>
    ...

我需要替换第一个 </addresses>下面是第一个<addresses xmlns="namespace">通过 </namespace:addresses>这样文件就变成了:

    some text
    <addresses>
      <something/>
    </addresses>
    some more text
    <addresses xmlns="namespace">
      <could be anything/>
    </namespace:addresses>
    some other text
    <addresses>
      <something else/>
    </addresses>
    ...

我知道 this similar thread ,但以下解决方案均未改变任何内容:

sed -e '/<addresses xmlns="namespace">/!b' -e ':a' -e "s/<\/namespace:addresses>/<\/addresses>/;t trail" -e 'n;ba' -e ':trail' -e 'n;btrail' file.xml
sed -e "/<addresses xmlns=\"namespace\">/,/./  s/<\/namespace:addresses>/<\/addresses>/" file.xml
sed -e "/<addresses xmlns=\"namespace\">/,/<\/namespace:addresses>/  s/<\/namespace:addresses>/<\/addresses>/" file.xml

例如:

sed -e "/<addresses xmlns=\"namespace\">/,/./  s/<\/namespace:addresses>/<\/addresses>/" file.xml
    some text
    <addresses>
      <something/>
    </addresses>
    some more text
    <addresses xmlns="namespace">
      <could be anything/>
    </addresses>
    some other text
    <addresses>
      <something else/>
    </addresses>
    ...

也许这个问题与我正在使用的 sed 有关:impish/21.10 上的 4.7-1ubuntu1 甚至 4.8-1。

有什么建议吗? 我愿意使用任何其他工具 (perl/awk),越简单越好。

最佳答案

perl 更容易比sed :

perl -0777 -i -pe 's~<(addresses)\s+xmlns="namespace">[^<]*(?:<(?!/\1>)[^<]*)*\K</\1>~</namespace:$1>~' file

参见 online demo . 详细信息:

  • <(addresses)\s+xmlns="namespace">[^<]*(?:<(?!/\1>)[^<]*)*\K</\1> - 正则表达式模式匹配
    • < - 一个 <字符
    • (addresses) - 第 1 组($1):addresses
    • \s+ - 一个或多个空格
    • xmlns="namespace"> - 一个固定的字符串
    • [^<]*(?:<(?!/\1>)[^<]*)* - 比 (?s:.)*? 更快的替代方案- 基本上,匹配不超过 </addresses> 的任何文本字符串
    • \K - 匹配重置运算符,它会忽略当前匹配内存缓冲区中到目前为止匹配的所有文本
    • </\1> -(这是最终消耗的并将被替换的):</ + 第 1 组值(以免重复 addresses ) + >
  • </namespace:$1> - 替换为 </namespace: + 第 1 组值 + > .

它取代了第一次出现,因为 -0777将文件变成一个多行文本,没有 g旗帜。

注意 \1 之间的区别模式内部的反向引用语法和 $1 perl 中替换模式中的替换反向引用命令。

参见 online demo :

s='    some text
    <addresses>
      <something/>
    </addresses>
    some more text
    <addresses xmlns="namespace">
      <could be anything/>
    </addresses>
    some other text
    <addresses>
      <something else/>
    </addresses>
    ...'
perl -0777 -pe 's~<(addresses)\s+xmlns="namespace">[^<]*(?:<(?!/\1>)[^<]*)*\K</\1>~</namespace:$1>~' <<< "$s"

输出:

 some text
    <addresses>
      <something/>
    </addresses>
    some more text
    <addresses xmlns="namespace">
      <could be anything/>
    </namespace:addresses>
    some other text
    <addresses>
      <something else/>
    </addresses>
    ...

关于sed - 在第一个匹配行之后替换第一个出现的行,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/71738141/

相关文章:

linux - 如何使用sed从文本文件中获取变量?

sed - 多次使用 `sed` 而不用管道

linux - 在sed中按字母顺序对变量进行排序

linux - 如何在 shell 脚本中使用冒号从 mysql 中分离检索到的数据?

file - linux 中的列聚合

c - 将 C 代码中的左大括号移动到下一行?

php - 网站被黑,如何使用 SED/GREP 删除恶意代码

linux - 在 Bash 中查找并替换不间断空格字符

python - 查找两个占位符之间的所有内容并将其替换为变量的内容

linux - 如何使用 sed 在两个特定字符( [ ] 括号)之间获取 shell 中的文本?