PowerShell XML 查詢練習
0 |
書到用時方恨少,臨時有個需求要從 XML 查詢特定一筆資料,打算用 PowerShell 快速秒殺,卻卡住不知該怎麼寫,只能開了 Visual Studio 用 C# 搞定。
事後檢討,武功招式要能活用,得捲起袖子實際操演熟練,沒有看完教學上場就能出招制敵的好事兒,至少,我不是那種武學奇才,所以有了這篇。
以下是本次練習用的 test.xml:
<?xml version="1.0"?>
<root>
<meta>
<title>XML Sample</title>
<revisions>
<version no="1.0">2022-09-02</version>
<version no="1.1">2022-09-03</version>
</revisions>
</meta>
<category name="Language">
<item>C#</item>
<item>PowerShell</item>
<item>JavaScript</item>
</category>
<category name="OS">
<item>Windows</item>
<item>macOS</item>
<item>Linux</item>
</category>
</root>
在 PowerShell 讀取 XML 檔,最簡單的起手式是 [Xml]$xd = (Get-Content .\text.xml )
,$xd 將是一個 XmlDocument 物件,如果你不想學任何 PowerShell 技巧,用 .NET 寫法也成。例如:
[xml]$doc = Get-Content test.xml
# $doc 背後為 XmlDocument,可呼叫原有 API 以 .NET 方式操作
$meta = $doc.DocumentElement.FirstChild;
Write-Host '-- Revisions --' -ForegroundColor Cyan
foreach ($ver in $meta.SelectSingleNode('revisions').SelectNodes('version')) {
$ver.GetAttribute("no")
$ver.InnerText
}
Write-Host '-- Items --' -ForegroundColor Cyan
foreach ($item in $doc.SelectNodes('//item')) {
$item.InnerText
}
不過,PowerShell 有個內建指令 Select-Xml,有更簡單的寫法,以上邏輯可以簡化成:
[xml]$doc = Get-Content test.xml
Write-Host '-- Revisions --' -ForegroundColor Cyan
Select-Xml -Xml $doc -XPath '/*/*[1]/revisions/version' | ForEach-Object {
$_.Node.no
$_.Node.InnerText
}
Write-Host '-- Items --' -ForegroundColor Cyan
Select-Xml -Xml $doc -XPath '//item' | ForEach-Object {
$_.Node.InnerText
}
得到一模一樣的結果:
不過,.NET LINQ 寫多了,老覺得 XPath 寫法是上個世紀的產物,直接用名稱存取內容才是王道呀!
沒問題,PowerShell 也支援用 $doc.root.meta 直接找到 <meta> 結節,但要先了解一下規則。
以 test.xml 為例,$doc 有兩個屬性 xml 及 root。root 則有 meta 跟 category 兩個屬性,前者是一個節點物件,後者則是集合,category 因為包含 name Attribute,PowerShell 會貼心顯示成 {Laguage, OS},否則會是 {category, category}。
meta 下又有 title 及 revisions 兩個屬性;$doc.root.meat.revisions 內含的 <version> 集合會變成 version 屬性,因沒有 name Attribute,顯示為 {version, version}。
最後,$doc.root.meta.revisions.version 會展開集合,每一筆包含 no Attribute 及 #text 內文。
另外,要存取節點內文,分為兩種情況,若節點沒有 Attribute,給節點名稱 PowerShell 便會傳回字串;若則會傳回節點物件,包含 Attribute 及 #text,此時要用 InnerText 取值:
綜合以上,將程式再簡化為:
[xml]$doc = Get-Content test.xml
Write-Host '-- Revisions --' -ForegroundColor Cyan
$doc.root.meta.revisions.version| ForEach-Object {
$_.no
$_.InnerText
}
Write-Host '-- Items --' -ForegroundColor Cyan
$doc.root.category | ForEach-Object {
$_.item
}
練習完畢。
Tips of using PowerShell to parse and query XML.
Comments
Be the first to post a comment