【问题标题】:How to get a specific string from xml in powershell如何在powershell中从xml获取特定字符串
【发布时间】:2020-03-20 15:15:31
【问题描述】:

我遇到了一个问题。我想从 XML 内容中获取特定的 idstring of product 节点。

示例:
运行 $xml.group.product[0].id 它返回 1234/5678
但我只想让$variable="1234" 匹配id = "1234/5678" 以获取或返回product 节点数组的索引

我该怎么做?

$xml=[xml]@"
<?xml version="1.0" encoding="UTF-8"?>
<group>
  <product description="phone" id="1234/5678">
     <item name="apple" version="50" />
     <item name="banana" version="100" />
  </product>
  <product description="notebook" id="6666/7777">
     <item name="orange" version="150" />
  </product>
</group>
"@

【问题讨论】:

  • 如果您的问题尚未得到完全解答,请提供反馈。

标签: xml powershell scripting windows-scripting


【解决方案1】:

如果我理解正确,您想根据特定 id 检索产品吗?

您可以使用Where-Object cmdlet 执行此操作。示例:

$xml.group.product | Where-Object { $_.id -eq '1234/5678'}

【讨论】:

    【解决方案2】:

    我想有几种方法可以快速做到这一点。

    $xml.group.product | Where-Object{$_.id -match "^1234/"}
    

    这基本上意味着从 1234 开始,应该输出类似:

    描述ID


    电话 1234/5678

    如果你只想要第一部分,你可能不得不把它分开:

    ($xml.group.product | Where-Object{$_.id -match "^1234/"}).id.Split("/")[0]
    

    了解数字的两部分的含义可能会有所帮助。如果打算在整个程序中使用第一部分,则可能值得将其添加为自己的属性。

    哦,如果你真的想返回一个布尔值,RegEx 匹配仍然有效。但它看起来更像:

    '1234/5678' -match '^1234/'
    

    这将返回 true,但如果您有多个产品,则 If 逻辑必须在某个地方循环。在到目前为止的答案中,循环和 if 逻辑由 Where 子句隐含。

    如果有帮助请告诉我...

    【讨论】:

      【解决方案3】:

      执行文字前缀匹配的最简单(但区分大小写)方法是使用[string] 类型的.StartsWith() 方法。

      您可以将其与.Where() array method (PSv4+) 结合使用,以查找其id 属性值以给定前缀开头的第一个匹配元素,以及其兄弟元素中的(从零开始的)索引。

      # The prefix to match.
      $variable = '1234'
      
      # Initialize the matched element's index to -1
      # which signals that no element was found.
      $ndx = -1
      
      # Use the .Where() method to find the (first) matching element.
      $el = $xml.group.product.Where({ $_.id.StartsWith($variable); ++$ndx }, 'First')
      

      注意:如果您使用的是 Windows PowerShell(相对于 PowerShell 6+),最好使用 @($xml.group.product).Where(...) 以确保 .Where() 方法调用甚至可以正常工作如果恰好只有 一个 product 元素(谢谢,Steven)。

      使用您的示例 XML,

      • $ndx 现在包含 0(表示 first product 元素匹配),
      • 和$el 包含匹配元素,
        &lt;product description="phone" id="1234/5678"&gt;...&lt;/product&gt;。

      关于 PowerShell 对标量和集合的统一处理:

      PowerShell [Core] 始终如一地支持 .Where() 和 .ForEach() array 方法,即使在 标量 上也是值得称道的。

      这种统一处理还扩展到:

      • 具有隐含.Count 属性的标量报告1(例如,$scalar = 42; $scalar.Count 报告1),明智地将标量视为单元素 集合。

      • 支持索引(例如,$scalar[0] 和 $scalar[-1] 与 $scalar 相同)。

        • 但是,自身支持索引的标量(从 PowerShell 角度)抢占此 PowerShell 提供的索引,例如在 XmlElement 实例的情况下,其自己的索引提供对子元素的访问按名称。 This GitHub issue 建议 PowerShell 提供的 位置 索引在这种情况下仍然可以工作,只要它不与类型原生索引冲突。

      【讨论】:

        【解决方案4】:

        考虑讨论:

        $Index = $null
        $Products = @($xml.group.product)
        $Product = 
        :loop For($i = 0; $i -lt $Products.Length; ++$i)
        {
            If($Products[$i].id -match "^1234/") {
                $Products[$i]
                $Index = $i
                break loop
            }
        }
        
        Write-Host "Last index : $Index"
        $Product
        

        从 mklement0 中识别出 -1 技巧,但仍在玩弄。我确实遇到了我提到的相同问题,如果恰好只有 1 个产品,则没有长度属性,因此循环甚至不会运行。我不得不用 @() 来解决这个问题。我无法翻转到字符串,因为 0 将始终是 -lt .length。所以我认为我在休息后吐出 $i 很聪明,但它会经常错误地为 0。

        所以我不得不使用 $Index。从概念上讲,如果 $Index 为 $null 您没有受到打击与如果 $ndx 为 -1 您没有受到打击,这并没有太大的不同。这也迫使我在 For 语句中引入 $Products 作为包装在 @() 中也显示了一些问题。

        所以这不那么简洁......只是一个练习 mklement0 的答案可能会更快。请原谅我继续说下去……

        【讨论】:

        • 请注意,您不需要循环标签 (:loop),因为没有嵌套循环。
        猜你喜欢
        • 2021-09-26
        • 2017-07-28
        • 2016-08-23
        • 2016-07-17
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多