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

Replacing first occurrence line after first matched line

让我们假设以下 XML 文件:

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

我需要将第一个 <addresses xmlns="namespace"> 之后的第一个 </addresses> 替换为 </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>
    ...

我知道 ,但是 none 以下解决方案改变了一切:

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),越简单越好。

perlsed 容易得多:

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

参见 online demo详情:

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

它取代了第一次出现,因为 -0777 将文件压缩成一个多行文本并且没有 g 标志。

请注意模式内的 </code> 反向引用语法与 <code>perl 命令中替换模式中的 </code> 替换反向引用之间的区别。</p> <p>参见<a href="https://ideone.com/RNRzWK" rel="nofollow noreferrer">online demo</a>:</p> <pre><code>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">[^<]*(?:<(?!/>)[^<]*)*\K</>~</namespace:>~' <<< "$s"

输出:

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