Linux

查找(子標籤的)模式並替換 XML 文件中的整個父標籤,而不使用 sed 工具

  • December 30, 2021

有沒有辦法使用正則表達式找到(子標籤的)模式並替換整個父標籤?我在沒有圖形環境的 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>, where是and*之間所有文本或程式碼的萬用字元<book``<author>...

我有一個想法,使用 Perl,考慮這些邏輯步驟:

  1. 搜尋模式所在的行號
  2. 辨識父塊打開和關閉標籤的行號
  3. 刪除這些行內的所有這些內容。
  4. 在這些行內添加新塊

但是,有可能,我更喜歡第一種方法。

處理結構化文件格式時,請使用專門為處理這些格式而設計的工具。正則表達式主要用於匹配文本,XML 文件並不是真正的文本,而是以特定方式結構化的數據(換行符等並不總是很重要)。同樣,sed它是一種用於處理文本行的工具,這也不是一般的 XML。

xqhttps://kislyuk.github.io/yq/使用

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

這用於xq將您的bookstoreXML 和您想要插入 (in insert.xml) 的元素轉換為 JSON。然後它將特定jq表達式應用於生成的 JSON 文件以提取.bookstore.book數組的每個條目。然後,該數組中具有.author等於的欄位的每個元素J K. Rowling都將替換為從 中讀取的元素insert.xml

更詳細地說:我們將新對象的內容讀.book入一個名為的內部變數$new中,然後通過呼叫input. 該select()語句作用於.bookstore.book數組的每個單獨元素,並提取具有特定作者的元素。其結果是這些匹配book條目的許多“路徑”。這些使用|=(更新運算符)更新為$new之前創建的值。

如果您想在命令行上而不是通過文件提供新的 XML,請使用 here-document:

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"
 }
}

我的首選方法往往是用來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/>子樹並附加一個新子樹,但這可能會破壞順序處理,所以我在這裡沒有這樣做。

引用自:https://unix.stackexchange.com/questions/682660