![查找(子标签的)模式并替换 XML 文件中的整个父标签,而不使用 sed 工具](https://linux22.com/image/199769/%E6%9F%A5%E6%89%BE%EF%BC%88%E5%AD%90%E6%A0%87%E7%AD%BE%E7%9A%84%EF%BC%89%E6%A8%A1%E5%BC%8F%E5%B9%B6%E6%9B%BF%E6%8D%A2%20XML%20%E6%96%87%E4%BB%B6%E4%B8%AD%E7%9A%84%E6%95%B4%E4%B8%AA%E7%88%B6%E6%A0%87%E7%AD%BE%EF%BC%8C%E8%80%8C%E4%B8%8D%E4%BD%BF%E7%94%A8%20sed%20%E5%B7%A5%E5%85%B7%20.png)
有没有办法使用正则表达式找到(子标签的)模式并替换整个父标签?我在没有图形环境的 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
我的首选方法往往是使用xmlstarlet
XML 数据进行操作。我们声明一个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 文档以提取数组的每个条目。然后,该数组中具有完全等于字段的每个元素都将替换为从 读取的元素。bookstore
insert.xml
jq
.bookstore.book
.author
J K. Rowling
insert.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"
}
}