查找(子标签的)模式并替换 XML 文件中的整个父标签,而不使用 sed 工具

查找(子标签的)模式并替换 XML 文件中的整个父标签,而不使用 sed 工具

有没有办法使用正则表达式找到(子标签的)模式并替换整个父标签?我在没有图形环境的 Linux 服务器上工作。

我有像这样的XML:

<?xml version="1.0" encoding="UTF-8"?>  
<bookstore>  
  <book category="COOKING">  
    <title lang="en">Everyday Italian</title>  
    <author>Giada De Laurentiis</author>  
    <year>2005</year>  
    <price>30.00</price>  
  </book>  
  <book category="CHILDREN">  
    <title lang="en">Harry Potter</title>  
    <author>J K. Rowling</author>  
    <year>2005</year>  
    <price>29.99</price>  
  </book>  
  <book category="WEB">  
    <title lang="en">Learning XML</title>  
    <author>Erik T. Ray</author>  
    <year>2003</year>  
    <price>39.95</price>  
  </book>  
</bookstore>  

我需要一个能够找到模式的 shell 脚本:

<author>J K. Rowling</author>

然后替换其完整块:

  <book category="CHILDREN">  
    <title lang="en">Harry Potter</title>  
    <author>J K. Rowling</author>  
    <year>2005</year>  
    <price>29.99</price>  
  </book>  

和:

  <book category="CHILDREN">  
    <title lang="en">Hamlet</title>  
    <author>William Shakespeare</author>  
  </book>

最终得到:

<?xml version="1.0" encoding="UTF-8"?>  
<bookstore>  
  <book category="COOKING">  
    <title lang="en">Everyday Italian</title>  
    <author>Giada De Laurentiis</author>  
    <year>2005</year>  
    <price>30.00</price>  
  </book>  
  <book category="CHILDREN">  
    <title lang="en">Hamlet</title>  
    <author>William Shakespeare</author>  
  </book>  
  <book category="WEB">  
    <title lang="en">Learning XML</title>  
    <author>Erik T. Ray</author>  
    <year>2003</year>  
    <price>39.95</price>  
  </book>  
</bookstore> 

类似于<book*<author>J K. Rowling</author>*</book>,其中 是和*之间所有文本或代码的通配符<book<author>...

我有一个想法,使用 Perl,考虑这些逻辑步骤:

  1. 搜索模式所在的行号
  2. 识别父块打开和关闭标签的行号
  3. 删除这些行内的所有内容。
  4. 在这些行内添加新块

但是,有可能,我更喜欢第一种方法。

答案1

我的首选方法往往是使用xmlstarletXML 数据进行操作。我们声明一个xmlstarlet变量$book来引用我们需要编辑的子树

xmlstarlet <682660.xml ed                                               \
    --var book '//book[author="J K. Rowling"]'                          \
    --update '$book' --value ''                                         \
    --update '$book/@category' --value 'CHILDREN'                       \
    --subnode '$book' --type 'elem' --name 'title'  --value 'Hamlet'    \
    --subnode '$book/title' --type attr --name 'lang' --value 'en'      \
    --subnode '$book' --type 'elem' --name 'author' --value 'William Shakespeare'

输出

<?xml version="1.0" encoding="UTF-8"?>
<bookstore>
  <book category="COOKING">
    <title lang="en">Everyday Italian</title>
    <author>Giada De Laurentiis</author>
    <year>2005</year>
    <price>30.00</price>
  </book>
  <book category="CHILDREN">
    <title lang="en">Hamlet</title>
    <author>William Shakespeare</author>
  </book>
  <book category="WEB">
    <title lang="en">Learning XML</title>
    <author>Erik T. Ray</author>
    <year>2003</year>
    <price>39.95</price>
  </book>
</bookstore>

您也可以删除相关的<book/>子树并附加一个新的子树,但这可能会破坏顺序处理,因此我在这里没有这样做。

答案2

使用结构化文档格式时,请使用专门用于处理这些格式的工具。正则表达式主要用于匹配文本,而 XML 文档并不是真正的文本,而是以特定方式构造的数据(换行符等并不总是重要的)。同样,sed它是一种处理文本行的工具,但这也不是 XML 的一般含义。

使用xq来自https://kislyuk.github.io/yq/

xq -x '.book as $new | input |
    (
        .bookstore.book[] |
        select(.author == "J K. Rowling")
    ) |= $new' insert.xml file.xml

这用于将 XML 和要插入(在 中)的元素xq转换为 JSON。然后,它将特定表达式应用于生成的 JSON 文档以提取数组的每个条目。然后,该数组中具有完全等于字段的每个元素都将替换为从 读取的元素。bookstoreinsert.xmljq.bookstore.book.authorJ K. Rowlinginsert.xml

更详细地说:我们将新对象的内容读取.book到名为 的内部变量中$new,然后通过调用 来获取主文档input。该select()语句作用于.bookstore.book数组的每个单独元素,并提取具有特定作者的元素。其结果是这些匹配条目的许多“路径” book。使用|=(更新运算符)将它们更新为$new之前创建的值。

如果您想在命令行上而不是通过文件提供新的 XML,请使用此处文档:

xq -x '.book as $new | input |
    (
        .bookstore.book[] |
        select(.author == "J K. Rowling")
    ) |= $new' - file.xml <<'NEW_XML'
<book category="CHILDREN">
  <title lang="en">Hamlet</title>
  <author>William Shakespeare</author>
</book>
NEW_XML

请注意,输入文件名insert.xml在命令行上被替换为破折号。

根据您问题中的数据,结果将是

<bookstore>
  <book category="COOKING">
    <title lang="en">Everyday Italian</title>
    <author>Giada De Laurentiis</author>
    <year>2005</year>
    <price>30.00</price>
  </book>
  <book category="CHILDREN">
    <title lang="en">Hamlet</title>
    <author>William Shakespeare</author>
  </book>
  <book category="WEB">
    <title lang="en">Learning XML</title>
    <author>Erik T. Ray</author>
    <year>2003</year>
    <price>39.95</price>
  </book>
</bookstore>

如果您使用其(或) 选项,该xq实用程序可以进行就地编辑。--in-place-i


作为参考,xq将您的 XML 转换为以下内部 JSON 表示形式,然后由 处理jq

{
  "bookstore": {
    "book": [
      {"@category":"COOKING","title":{"@lang":"en","#text":"Everyday Italian"},"author":"Giada De Laurentiis","year":"2005","price":"30.00"},
      {"@category":"CHILDREN","title":{"@lang":"en","#text":"Harry Potter"},"author":"J K. Rowling","year":"2005","price":"29.99"},
      {"@category":"WEB","title":{"@lang":"en","#text":"Learning XML"},"author":"Erik T. Ray","year":"2003","price":"39.95"}
    ]
  }
}

要插入的数据将转换为等价的内容

{
  "book": {
    "@category": "CHILDREN",
    "title": {
      "@lang": "en",
      "#text": "Hamlet"
    },
    "author": "William Shakespeare"
  }
}

相关内容