【问题标题】:ForEach in XML file with PowerShell使用 PowerShell 的 XML 文件中的 ForEach
【发布时间】:2018-07-08 12:05:00
【问题描述】:

我在$DSConfigPath 有一个 XML 文件,其中包含以下信息:

<dataSources>
  <add id="DataSource1" type="Fixed" distance="1">
    <parameters>
      <name />
      <address>10.1.1.10</address>
      <otherIdentifiers>DataSource1</otherIdentifiers>
      <activeStatus>true</activeStatus>
      <ip>10.1.1.10</ip>
      <port>952</port>
    </parameters>
  </add>
  <add id="DataSource2" type="Fixed" distance="2">
    <parameters>
      <name />
      <address>10.1.1.11</address>
      <otherIdentifiers>DataSource2</otherIdentifiers>
      <activeStatus>true</activeStatus>
      <ip>10.1.1.11</ip>
      <port>952</port>
    </parameters>
  </add>
  <add id="DataSource3" type="Fixed" distance="3">
    <parameters>
      <name />
      <address>10.1.1.12</address>
      <otherIdentifiers>DataSource1</otherIdentifiers>
      <activeStatus>false</activeStatus>
      <ip>10.1.1.12</ip>
      <port>952</port>
    </parameters>
  </add>
</dataSources>

我的目标是对&lt;activeStatus&gt; 为“真”的任何 IP/端口进行端口连接测试。

我有以下函数,当我输入特定的 $hostname$port 时,我已经验证它会给我正确的结果:

function Test-Port($hostname, $port) {
    # This works no matter in which form we get $host - hostname or ip address
    try {
        $ip = [System.Net.Dns]::GetHostAddresses($hostname) | 
              Select-Object IPAddressToString -ExpandProperty IPAddressToString
        if ($ip.GetType().Name -eq "Object[]") {
            #If we have several ip's for that address, let's take first one
            $ip = $ip[0]
        }
    } catch {
        Write-Host "Possibly $hostname is wrong hostname or IP"
        return
    }
    $t = New-Object Net.Sockets.TcpClient
    # We use Try\Catch to remove exception info from console if we can't connect
    try {
        $t.Connect($ip,$port)
    } catch {}

    if ($t.Connected) {
        $t.Close()
        $msg = "Port $port is operational"
    } else {
        $msg = "Port $port on $ip is closed, "
        $msg += "You may need to contact your IT team to open it. "                                 
    }
    Write-Host $msg
}

所以现在当我添加以下变量时:

[xml]$DSConfig = gc "$DSConfigPath"

$DS = $dsconfig.datasources.add.parameters
$DSName = $DS.otherIdentifiers
$DSIP = $DS.ip
$DSPort = $DS.port
$DSActive = $DS | Where-Object {$_.activeStatus -eq 'True'}

$hostname = $DSIP   # yes I realize this is redundant
$port = $DSPORT     # and yes, I realize this is redundant as well

然后运行:

foreach ($DSActive in $DSConfig) {Test-Port $hostname $port}

我得到了结果:

可能 10.1.1.10 10.1.1.11 是错误的主机名或 IP

有什么建议可以让我测试到 10.1.1.10:952 的连接,给出结果,然后测试到 10.1.1.11:952 的连接,然后给出结果?

【问题讨论】:

  • foreach ($DSActive in $DSConfig) 没有意义 - 您刚刚在前一个块中将 $DSConfig 定义为 [xml] 文档。请发帖an mvce
  • 谢谢...诚然我不知道自己在做什么...这就是我来这里寻求帮助的原因...
  • 如果您能确保您发布的示例 XML 确实有效(如“不缺少一对尖括号”),将会有所帮助。

标签: xml powershell foreach


【解决方案1】:

使用SelectNodes()从xml文档中抓取&lt;parameters&gt;节点:

# Read XML document
[xml]$DSConfig = gc "$DSConfigPath"

# Select <parameters> nodes
$ParametersNode = $DSConfig.SelectNodes('//parameters')

# Loop over selected nodes
foreach($Node in $ParametersNode){
  # Test if activeStatus == 'true'
  if($Node.activeStatus -eq 'true') {
    # Run the Test-Port command
    Test-Port $Node.ip -port $Node.port
  }
}

【讨论】:

    【解决方案2】:

    使用Select-Xml 的替代方法。这实际上与 Mathias 的答案相同,但使用 cmdlet 而不是方法。不是特别有用,除非您想利用该方法不提供的 cmdlet 中内置的一些其他选项(在此处不是真正有用,但在更复杂的用途中绝对有用)。

    # Read XML document
    [xml]$DSConfig = gc "$DSConfigPath"
    
    # Select <parameters> nodes
    $ParametersNode = Select-Xml -Xml $DSConfig -XPath '//parameters'|% {
        Test-Port $_.Node.ip $_.Node.port
    }
    

    【讨论】:

      【解决方案3】:

      声明

      $DS = $dsconfig.datasources.add.parameters
      

      将所有&lt;parameter&gt; 节点的列表放入变量$DS。如果您检查$DS.Count,您会看到样本数据的值为 3,如果您回显变量,您会看到如下内容:

      PS C:\> 写入输出 $DS 姓名 : 地址:10.1.1.10 其他标识符:DataSource1 活动状态:真 ip:10.1.1.10 端口:952 姓名 : 地址:10.1.1.11 其他标识符:DataSource2 活动状态:真 ip:10.1.1.11 端口:952 姓名 : 地址:10.1.1.12 其他标识符:DataSource1 活动状态:假 ip : 10.1.1.12 端口:952

      接下来的陈述

      $DSIP = $DS.ip
      $DSPort = $DS.port
      

      分别用所有 IP 地址和所有端口的列表填充变量 $DSIP$DSPort

      PS C:\> 写入输出 $DSIP 10.1.1.10 10.1.1.11 10.1.1.12

      在 PowerShell v3 及更高版本上,即。在 PowerShell v3 之前,这些语句会引发错误,因为旧版本不支持 member enumeration,即当数组对象本身不支持时访问数组的 元素 上的属性和方法属性或方法。

      当您将此 IP 地址列表传递给 Test-Port 时,声明

      $ip = [System.Net.Dns]::GetHostAddresses($hostname)
      

      MethodInvocationException 失败,因为GetHostAddresses() 需要一个字符串,而不是字符串数组。

      还有,你的循环

      foreach ($DSActive in $DSConfig) {Test-Port $hostname $port}
      

      将在一次迭代后终止,因为 $DSConfig 只有一个元素:根节点 (&lt;dataSources&gt;)。

      要让您的代码为每个 IP 地址和端口调用 Test-Path,请将上述循环更改为:

      foreach ($DSActive in $DSConfig.datasources.add.parameters) {
          Test-Port $DSActive.ip $DSActive.port
      }
      

      【讨论】:

        猜你喜欢
        • 2013-07-09
        • 2018-10-28
        • 2011-08-23
        • 2015-06-30
        • 1970-01-01
        • 1970-01-01
        • 2021-03-29
        • 2021-01-02
        • 2020-12-30
        相关资源
        最近更新 更多