Windows PowerShell进阶操作XML

上篇我们说了powershell批量上传七牛云文件的时候,用到了XML交互,最近一个星期专门看了些文章来把这一知识点梳理下,下面是我看到的比较好的一篇全面讲解XML操作的文章,在翻译的基础上加入了自己的一些看法在里面,希望大家看完有所收获。当然本文需要一点前端的知识比如XML/XPath,还要一点C#的基础。

PowerShell具有出色的XML支持。 一开始并不是很明显,但是在这篇文章的讲解后,您很快就能解决日常的XML任务 - 甚至是非常复杂的任务。

因此,让我们看一下如何使用非常简单的PowerShell代码来完成以前在PowerShell之前的时代所做的事情。

让我们从头开始创建XML文档,添加新数据集,更改信息,添加新数据,删除数据,并将更新版本保存到新格式良好的XML文件中。

新建XML

从头开始创建全新的XML文档曾经是一项繁琐的工作。 许多脚本编写者都使用纯文本创建XML文件。 虽然没关系,但它很容易出错。 可能是错别字和大小写的问题,您可能会发现自己处于一个不友好的世界,它充满了格式错误且功能失常的XML。

再也不会了,因为有一个伙伴可以帮助您创建XML文档:XMLTextWriter对象。 它隐藏了底层处理原始XML对象模型的复杂性,而是帮助您将各种信息写入XML文件中。

编者: 原文是13年写的,所以有些技术可能已经更新了,比如我们访问微软的XMLTextWriter页面,其已经推荐使用代替。这里我们主要了解这篇文章的思想即可,老的依然可以用,详细的使用方法大家可以去看微软官方文档,文末有链接。使用的方法其实之前都有说过的,相信理解起来应该不难。

33-1 XmlTextWriter Class

为了开始这个故事,让我们创建一个相当复杂的XML文档,即将到来的示例可以使用它。 目标是创建一个包含所有典型内容的XML文档:节点,属性,数据部分和注释。

此脚本生成包含大量随机信息的虚假服务器清单。 结果在记事本中打开,看起来与此类似:

这个XML文档的目的有两个:它是一个如何从头开始创建XML文件的示例,它可以作为以下练习的示例数据。

假设这是一个包含相关信息的XML文件。 您可以将要学习的策略应用于任何格式良好的XML文件。

注意:XMLTextWriter为您提供了很多魔力,但您有责任创建有意义的内容。 容易让人头疼的问题之一是格式错误的节点名称。 节点名称不得包含空格。

因此,虽然“CodeSegment”没问题,但“Code Segment”也不行。 XML会尝试将您的节点命名为“Code”,然后添加一个名为“Segment”的属性,最后会阻止您从未为该属性(“CodeSegment”)赋值。

读取XML

一个常见任务是从XML文件中提取信息。 假设您需要一个机器列表及其IP地址。 如果您已生成上面的示例XML文件,那么这就是创建报告所需的全部内容:

结果看起来与此类似:

注意:有些人可能想知道为什么我上面最初示例使用XML对象。 通常你会发现这样的代码:

最简单的原因就是二者之间的性能。 通过Get-Content将XML文件作为纯文本文件读取,然后在第二步中将其转换为XML是一种非常昂贵的方法。 即使我们的XML文件不是那么大,后一种解决方案所花费的时间几乎是第一种解决方案的7倍,而随着XML文件内容的增加,相应时间也会增加。

因此,只要您想加载XML文件,请确保获取XML对象并使用其Load()方法。 通过接受URL的方式,这种方法是通用的,因此您也可以使用URL到您喜欢的RSS源 - 前提是您可以直接访问Internet并且无需配置代理设置。

编者:关于上面原文提到的这个性能这一点,我这里稍微提一下,其实我自己一直在用这种方法来加载和读取XML。关于这个时间性能的开销,我自己就用上面创建好的XML文件测试了一下。可以看到用的方式是比的方式快了2.4倍,但是这个单位是毫秒,如果我们平常的使用不在乎这1,2毫秒的差别,就怎么使用方便怎么来吧,也不是说就必须只能用的方式。大家自己要根据实际开发环境取舍。

读取特定节点

假设您不想要所有服务器的列表,而只想查找列表中特定服务器的IP地址和信息属性info1。 您可以使用相同的方法:

这将获得“server0009”的IP地址加上info1属性。 您可以使用XPath(一种XML查询语言)来直接查询和获取特定节点,而不用像上面那样去筛选找到特定的节点。

重要说明:XPath区分大小写,因此如果节点名称为“Machine”,则无法查询“machine”。

XPath是一种非常强大的XML查询语言。 您可以在Internet上找到有关其语法的信息(请查看以下链接,例如:http://www.w3schools.com/xpath/和http://go.microsoft.com/fwlink/?LinkId=143609)。 当您阅读这些文档时,您会发现XPath也可以使用所谓的“用户定义函数”,如last()或lowercase(),这里就不详细介绍了。

编者:关于上面原文所提到的这个XPath区分大小写问题,我们知道powershell是不区分大小写的,那么我们要怎么在使用powershell获取XML节点时可以不区分大小写呢? 为了解决这个问题,目前结合我自己会的和Google之后有2个方法,下面结合这个我创建的奇怪的XML来测试说下,这个XML的所有子节点其实都是类似的,只不过是大小写之分。

节点名称 :tag/TAG

属性名称 :class/CLASS

属性值 :abc/ABC

节点值 :全小写的 tag|class|abc --> 全大写的 TAG|CLASS|ABC

针对单节点,不使用XPath,而是用属性的方式去获取。我们使用或者可以获取到所有名称是tag/TAG的子节点,无论大小写, 如果用单纯的XPATH就不行,只能获取名称是小写tag的子节点。

使用XPATH的函数,下面效果和上面一致,可以获取到所有名称是tag的子节点,无论大小写。

下面是获取属性名称是class/CLASS的所有节点,不区分大小写。

另:我在准备这篇文章的这一周内,才知道原来XML也有namespace这一概念,还是平时这一块用的少,知识盲区。文末有参考链接,感兴趣的可以了解下,作为课外拓展。

更新XML内容

通常,您需要更新XML文档中的信息。 而不是自己解析XML,只需坚持你刚刚学到的技术。因此,如果您想更新Server0006并为其分配新名称和不同的IP地址,那么您可以这样做:

如您所见,更新信息很简单,您所做的所有更改都会自动应用于基础XML对象。 您需要做的就是将更改的XML对象保存到文件中以使更改成为永久更改。 结果显示在记事本编辑器中,看起来类似于:

您刚刚对现有XML文档进行了更改,没有棘手的解析,也没有破坏XML结构的风险。以同样的方式,您可以进行批量调整。 让我们假设所有服务器都要获得全新的名称。 而不是“ServerXXXX”,现在需要将机器命名为“Prod_ServerXXXX”。 这是解决方案:

请注意XML文档中的所有服务器名称是如何更新的。 Select-XML这次不会返回一个对象,而是多个,每个服务器一个。 这是因为XPath这次选择所有“机器”节点而没有特殊过滤。 这就是为什么所有这些节点都需要在foreach循环中处理的原因。

在循环内部,为节点“name”分配一个新值,并且一旦更新了所有“Machine”节点,就会在记事本中保存并打开XML文档。

您可能会争辩说,在此示例中,在服务器名称前添加“Prod_”实际上是一个微不足道的变化,这是事实。 可能有更复杂的要求。但是,这里的重点是展示如何从根本上改变XML数据,而不是如何进行复杂的字符串操作。

但是,如果您问自己如何,例如,将“ServerXXXX”替换为“PCXX”(包括将4位数字转换为2位数字,这绝对不是一个微不足道的变化),这是一个解决方案:

这次,正则表达式提取原始服务器名称的数字部分,然后-f运算符重新格式化该数字并将其添加到新服务器前缀。

正则表达式和数字格式都不是本文的重点。 重要的是要看到您可以自由地使用您喜欢的任何技术来构建新的服务器名称。 但是,更改XML内容始终遵循相同的规则。

编者:关于更新节点数据,我曾经遇到了一个很奇怪的问题,就是在给节点赋新值的时候会报错:Cannot set "xxx" because only strings can be used as values to set XmlNode properties.后来查了下发现要用下面的方法才行,一般情况下这个方法是不会报错的,但是如果你真的遇到这个错误可以用下面的方法试着解决。

添加新的节点/数据

有时,更新数据是不够的。 您可能希望将新计算机添加到列表中。 同样,这很简单。 您只需选择一个现有节点,克隆它,然后更新其内容并将其附加到您喜欢的父节点。 这样,您不必自己创建复杂的节点结构,并且可以确定新节点的结构与任何现有节点一样。

这会将新机器添加到机器列表中:

由于要添加的节点是从现有节点克隆的,因此将从现有节点复制此新节点中的所有信息。 您未更新的信息将保留旧值。如果您想将新节点添加到列表顶部怎么办? 只需使用InsertBefore()而不是AppendChild():

同样,您基本上可以在任何地方插入新节点。 这将在Server0007之后插入:

删除XML节点

完全从XML文件中删除数据同样简单。 如果您想从列表中删除Server0007,请按以下步骤操作:

更加强大的功能

通过提供的示例,您现在可以在几行代码中管理最常用的XML操作。非常值得花一些时间来提高您的XML和XPath熟练程度 - 您可以用它们做出惊人的事情。

对于那些长期坚持关注我的人,我有一个小礼物给你:我经常使用的一个很棒的小工具,对你来说也很有帮助,我相信。它使用您刚才听到的完全相同的策略。这是故事:

ConvertTo-XML可以将任何对象转换为XML,并且由于XML是一种分层数据格式,保留了给定深度的结构,因此它是检查嵌套对象属性的绝佳方法。因此,您可以“展开”对象结构并查看其所有属性,甚至是深度嵌套的属性。

如果没有XML和XPath,您所能做的就是查看纯XML并自己搜索信息。例如,如果您想要找出$host对象存储PowerShell的颜色信息的确切位置,您可以这样做(毕竟这可能不是一个好主意,因为您充斥着原始XML信息):

通过刚刚介绍的知识,您现在可以获取原始XML并提取和过滤对象属性。

所以这里是承诺的函数Get-ObjectProperty,它有点像自带的Get-Member。 它可以告诉您对象中的哪个属性包含您所追求的值。 看一看:

这将返回中名称中包含“Color”的所有嵌套属性。控制台输出很可能被截断,因此最好在网格视图窗口中显示信息:

33-2 视窗视图

请注意“Path”列:此属性指定您将如何访问给定的嵌套属性。 在该示例中,在对象层次结构内深度走两层。 更深的深度将展示更多的信息,但也会使用更无关的噪声信息污染结果。

虽然您可以管道多个对象,但最好只管理一个对象,因为产生了大量的数据。 此行将列出流程对象中的所有嵌套属性,深度为五级,具有数值:

此行将返回类型为“String”的后台处理程序服务对象的所有嵌套属性:

这是Get-ObjectProperty的源代码。 考虑到它为你所做的工作,它比仅仅几行更复杂,但仍然非常短。

它使用刚刚解释过的完全相同的技术,所以一旦你对上面的简单例子感到满意,你也可以尝试消化这个 - 或者只是将它用作工具而不用担心它的XML魔法:

总结

相关图片等已上传https://gitee.com/chaoyuew/powershell/tree/feature/wechat/SPPS33,计算机语言看的再多不如动手,多敲敲代码。我之前工作中对XML的适用比较少,基本就停留在的使用上,这一星期自己也学了不少的东西,自己记录文章的过程也是自己学习的过程,共勉之。

参考链接

http://www.powershellmagazine.com/2013/08/19/mastering-everyday-xml-tasks-in-powershell/

https://msdn.microsoft.com/en-us/library/system.xml.xmltextwriter(v=vs.110).aspx

https://weblogs.asp.net/hajan/xpath-case-insensitivtity

https://becomelotr.wordpress.com/2014/02/09/case-insensitive-select-xml/

https://msdn.microsoft.com/en-us/library/ms256119(v=vs.110).aspx

https://www.dvteclipse.com/documentation/svlinter/How_to_use_special_characters_in_XML.3F.html

https://stackoverflow.com/questions/2462248/what-is-the-difference-between-name-and-local-name

https://stackoverflow.com/questions/47650002/find-xpath-attribute-name-contains-specific-string

http://www.w3school.com.cn/xml/xml_namespaces.asp

https://msdn.microsoft.com/en-us/library/ms950779.aspx?irgwc=1&OCID=AID681541_aff_7593_1243925&tduid=(ir_QFzUMIViAzjLR%3ArxXrwYWzqHUkjT65TaywXW2c0)(7593)(1243925)(TnL5HPStwNw-w4P_zcYwaLF7fqdTCULg5w)()&irclickid=QFzUMIViAzjLR%3ArxXrwYWzqHUkjT65TaywXW2c0

  • 发表于:
  • 原文链接https://kuaibao.qq.com/s/20180707G1K2X400?refer=cp_1026
  • 腾讯「云+社区」是腾讯内容开放平台帐号(企鹅号)传播渠道之一,根据《腾讯内容开放平台服务协议》转载发布内容。

扫码关注云+社区

领取腾讯云代金券