使用 sed 或 awk 在 XML 文件中搜尋替換
所以我有一個任務,我必須通過 bash shell 腳本來操作 XML 文件。
以下是步驟:
- 查詢 XML 文件中的值。
- 獲取該值並交叉引用它以從列表中查找新值。
- 用新值替換不同元素的值。
這是刪除了非必要資訊的 XML 範例:
<fmreq:fileManagementRequestDetail xmlns:fmreq="http://foobar.com/filemanagement"> <fmreq:property> <fmreq:name>form_category_cd</fmreq:name> <fmreq:value>Memos</fmreq:value> </fmreq:property> <fmreq:property> <fmreq:name>object_name</fmreq:name> <fmreq:value>Correspondence</fmreq:value> </fmreq:property> </fmreq:fileManagementRequestDetail>
我必須從 object_name 下的 value 元素中獲取值,交叉引用它,然後將 form_category_cd value 元素下的值替換為新值:
因此,如果 object_name -> value 為 Correspondence,則 form_category_cd -> value 可能需要為 YYZ。
這就是問題所在,我只能使用我們伺服器上可用的工具,因為我們的運營組將我們限制在手頭的工具上。更新 xmllint 是一場鬥爭,然後它被否決了。我使用的版本不支持 –xpath,相信我在美好的一天很難。此外,我可用的版本不支持名稱空間,因此 xmllint 已過時。
我嘗試過 sed,但它似乎不喜歡我的正則表達式,即使我嘗試的每個測試器都工作正常。
正則表達式:
(<fmreq\:name>object_name<\/fmreq\:name>)(?:\n\s*)(<fmreq\:value>)(.*)(<\/fmreq\:value>)
我需要獲得第 3 組,但 sed 不會返回它。相反,它返回 XML 文件的全部內容。
sed -e 's/\(<fmreq\:name>object_name<\/fmreq\:name>\)\(?:\n\s*\)\(<fmreq\:value>\)\(.*\)\(<\/fmreq\:value>\)/\3/' < c3.xml
我對 awk / gawk 不太熟悉,所以我也在努力弄清楚它們,但如果可以找到解決方案,我會向他們開放。
很想有一個 awk / gawk 解決方案只是為了讓老闆高興,因為他是一個老 awk 粉絲,但我會接受我能得到的,因為我很難過。
同樣,我必須使用手頭的工具並且無法安裝任何新工具。
我認為您的
sed
命令存在幾個問題:
- 您不使用該
-n
選項,因此預設情況下sed
僅將輸入的每一行列印到輸出(可能由sed
命令修改)。- 您不需要重定向
< c3.xml
,因為sed
將最後一個參數辨識為文件名。sed
不太適合多行匹配。例如,請參見此處。以下似乎適用於您的範例:
sed -n "/<fmreq:name>object_name<\/fmreq:name>/ {n;p}" c3.xml | sed "s/^\s*<fmreq:value>\(.*\)<\/fmreq:value>/\1/g"
或者,只有一次
sed
呼叫:sed -n "/<fmreq:name>object_name<\/fmreq\:name>/ {n;s/^\s*<fmreq:value>\(.*\)<\/fmreq:value>/\1/g;p}" c3.xml
該命令作用的細分:
- 該選項
-n
告訴sed
在處理完行後不要列印模式空間。因此,您需要p
明確地使用該命令來執行此操作。/regex/
告訴sed
只在匹配的行上執行後面的命令regex
。- 該
sed
命令n
將模式空間的內容替換為下一行輸入,即包含您感興趣的值的行。- 該
sed
命令用 替換模式空間中s/regex/replacement/
的第一個匹配項。regex``replacement
- 該
sed
命令p
列印該行。
使用XMLStarlet:
$ xml ed -u '//fmreq:property[fmreq:name="object_name"]/preceding-sibling::fmreq:property/fmreq:name' -v YYZ file.xml <?xml version="1.0"?> <fmreq:fileManagementRequestDetail xmlns:fmreq="http://foobar.com/filemanagement"> <fmreq:property> <fmreq:name>YYC</fmreq:name> <fmreq:value>Memos</fmreq:value> </fmreq:property> <fmreq:property> <fmreq:name>object_name</fmreq:name> <fmreq:value>Correspondence</fmreq:value> </fmreq:property> </fmreq:fileManagementRequestDetail>
XPath 的第一部分,
//fmreq:property[fmreq:name="object_name"]
將定位<fmreq:name>object_name</fmreq:name>
節點,/preceding-sibling::fmreq:property/fmreq:name
位將定位<fmreq:name>
前一個節點的<fmreq:property>
節點。