【问题标题】:Powershell Variables for XML node/path addressing用于 XML 节点/路径寻址的 Powershell 变量
【发布时间】:2022-11-10 03:36:26
【问题描述】:

您好亲爱的 Powershell 专家,

我需要一些建议。我尝试使用 Powershell 变量(我从参数中获取)来处理 XML 路径/节点。

我的情景

下面的代码显示了我的代码的简短剪辑。有了这个切口,我将尝试尽可能详细地解释我的问题。

foreach ($Gpo in $AllGpos) {
    [xml]$selected_Gpo = Get-GPOReport -ReportType Xml -Guid $Gpo.Id
    $policy_object = $selected_Gpo.GPO.Computer.ExtensionData.Extension
        if ($policy_object.$Policy_Identity_XMLPath_Name -eq $Policy_Identity_Name) {
                if ($policy_object.$Setting_XMLPath_Name -eq $Policy_Identity_Setting_Name -and $policy_object.$Setting_XMLPath_Value -eq $Policy_Identity_Setting_Value) {
                }
                ...

我遍历所有组策略对象(在 Active Directory 环境中),生成 GPO 的 xml 报告,然后在此 xml 文件中搜索特定的组策略设置。只要我使用硬编码的 xml 节点路径,这种方法就可以很好地工作。例如: 而不是使用“$policy_object.$Policy_Identity_XMLPath_Name -eq $Policy_Identity_Name”,我可以使用“$policy_object.Policy.DropDownList.Name -eq”示例设置。但我无法使用这些硬编码的 xml 节点路径并想使用变量来在 xml 文件中处理这些路径。

示例 XML 的快捷方式

<q1:Policy>
  <q1:Name>TestName</q1:Name>
  <q1:State>Enabled</q1:State>
  <q1:Explain>Test Explaination </q1:Explain>
  <q1:Supported>Testsupported version</q1:Supported>
  <q1:Category>Test Category</q1:Category>
  <q1:DropDownList>
      <q1:Name>Test Name DropDownList</q1:Name>
      <q1:State>Enabled</q1:State>
      <q1:Value>
          <q1:Name>TestName Value</q1:Name>
      </q1:Value>
  </q1:DropDownList>
   </q1:Policy>

我的问题

问题是,我无法使用 powershell 变量解决 xml 路径。当我使用上面代码示例中的变量 ($policy_object.$Policy_Identity_XMLPath_Name) 时,我会返回 null,因此我的 if 语句不再起作用。

到目前为止我做了什么

当然,我做了很多试验和错误的事情,用变量来获取 powershell 地址这些 xml 节点路径。我试图将 ($policy_object.$Policy_Identity_XMLPath_Name) 放在不同类型的引号中。 我也已经看到堆栈溢出帖子有一个非常相似的问题 (Parsing an XML file with PowerShell with node from variable) 但是这些解决方案基本上还不错,但在我的具体场景中没有用。 让我解释:

$Policy_Identity_Setting_XMLPath_Name = 'GPO/Computer/ExtensionData/Extension/SecurityOptions/Display/DisplayFields/Field/Name'
$test = policy_object.SelectNodes($Policy_Identity_Setting_XMLPath_Name)

=> 结果为空。不知道为什么说实话

$ExecutionContext.InvokeCommand.ExpandString("`$(`policy_object.$Policy_Identity_Setting_XMLPath_Name)")

这将导致“可接受的”结果。但在我的场景中,结果主要是多个值(GPO 设置),并将它们全部存储在一个巨大的字符串中。之后我无法访问此字符串中的特定值。所以这种方法也不理想,因为我无法进一步处理结果。

希望各位小伙伴有好的想法。也许我在我的大脑逻辑中让它变得比必要的更复杂。 如果您需要更多信息,请告诉我。

【问题讨论】:

    标签: xml powershell parsing variables gpo


    【解决方案1】:

    如果你想使用 PowerShell 对 XML DOM 的改编, 这使得使用. 运算符对元素和属性进行类似属性的访问,您可以深入了解您的 XML 文档迭代地通过使用.. 分隔的路径字符串拆分为其组件,利用您可以使用的事实表达式动态指定(单个)属性名称:

    # Sample input
    [xml] $xml = @'
    <q1:Policy xmlns:q1="http://example.org">
      <q1:Name>TestName</q1:Name>
      <q1:State>Enabled</q1:State>
      <q1:Explain>Test Explaination </q1:Explain>
      <q1:Supported>Testsupported version</q1:Supported>
      <q1:Category>Test Category</q1:Category>
      <q1:DropDownList>
        <q1:Name>Test Name DropDownList</q1:Name>
        <q1:State>Enabled</q1:State>
        <q1:Value>
          <q1:Name>TestName Value</q1:Name>
        </q1:Value>
      </q1:DropDownList>
    </q1:Policy>
    '@
    
    # Define the path as a single, dotted string; e.g.:
    $dottedPath = 'Policy.DropDownList.State'
    
    # Drill down into the XML, component by component.
    $result = $xml
    foreach ($prop in $dottedPath -split '.') { $result = $result.$prop }
    
    $result # Output the result: -> 'Enabled'
    

    注意:PowerShell 对 XML DOM 的改编很方便——尤其是不是需要指定命名空间(见下文) - 但有其局限性;见this answer


    相比之下,为了使用XPath查询,例如 with.SelectNodes()

    • 路径中的元素名称必须是/-分隔的
    • 并且,由于您的文档使用XML 命名空间, 你必须通过命名空间管理器实例作为第二个参数命名空间-限定路径中的元素名称

    如果你忽略了后者,你确实会得到$null 作为结果。

    # Sample input
    [xml] $xml = @'
    <q1:Policy xmlns:q1="http://example.org">
      <q1:Name>TestName</q1:Name>
      <q1:State>Enabled</q1:State>
      <q1:Explain>Test Explaination </q1:Explain>
      <q1:Supported>Testsupported version</q1:Supported>
      <q1:Category>Test Category</q1:Category>
      <q1:DropDownList>
        <q1:Name>Test Name DropDownList</q1:Name>
        <q1:State>Enabled</q1:State>
        <q1:Value>
          <q1:Name>TestName Value</q1:Name>
        </q1:Value>
      </q1:DropDownList>
    </q1:Policy>
    '@
    
    # Create a namespace manager and associate it with the document.
    $namespaceMgr = [System.Xml.XmlNamespaceManager]::new($xml.NameTable)
    
    # Add an entry for the 'q1' namespace prefix.
    # Note:
    #  * The *prefix* (entry *name*) need not match the prefix in the document,
    #    but whatever name you choose must be used in your XPath query.
    #  * The namespace *URL* must match the one in the document, however.
    $namespaceMgr.AddNameSpace('q1', 'http://example.org')
    
    # Construct the XPath path, using the appropriate namespace prefixes; e.g.:
    $xpath = 'q1:Policy/q1:DropDownList/q1:State'
    
    # Now call .SelectNodes() with the query and the namespace manager.
    # Note: This outputs the targeted XML element(s) as a whole, 
    #       not their inner text, as dot notation would do.
    #       Append .InnerText, if needed.
    $xml.SelectNodes($xpath, $namespaceMgr)
    

    注意:或者,考虑使用Select-Xml cmdlet,它为上面使用的.NET API 提供了更高级别的接口;有关示例,请参见 this answer

    【讨论】:

    • 非常感谢 mklement0 也提供了这个非常有用和详细的答案!实际上,我为您的第一个建议实施了一个非常相似的解决方案 # 逐个组件深入研究 XML。 $result = $xml foreach ($prop in $dottedPath -split '.') { $result = $result.$prop } $result # 输出结果:-> 'Enabled' => 但我一定会检查 XPath 和Select-XML 解决方案也是如此!
    • 很高兴听到答案有帮助,@LKo.exp。是的,XPath 解决方案值得考虑,因为它们不仅速度更快,而且提供复杂的查询功能。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多