2014-09-19 143 views
2

出於某種原因,如果我的XML文件只包含一個計算機節點,但它返回一個列表(這是我總是期望的),如果有的話,下面的Read-TestControllerXml powershell函數返回一個System.Xml.XmlElement類型的對象多個計算機節點。爲什麼這個Powershell函數有時會返回一個列表,而其他時間會返回一個XmlElement?

的XML文件的示例如下:

<test_controller> 
    <defaults> 
     <nunit path='C:\Program Files (x86)\NUnit 2.6.2\bin\nunit-console.exe' /> 
     <nunit_results local_path='C:\Test_Results' remote_path='C:\Test_Results' /> 
     <powershell path='C:\windows\system32\WindowsPowerShell\v1.0\powershell.exe' /> 
     <psexec path='C:\PSTools\PsExec.exe' /> 
    </defaults> 

    <platform os='windows' version='2012' cpu='x64'> 
     <computer hostname='VM-2012R2-03' alias='standalone-01' configuration='standalone' image='VM-2012R2-03:Clean' /> 
    </platform> 
</test_controller> 

這裏是PowerShell的功能:

#################################################################### 
# Parses the TestController.xml file and returns a list of Computer XML objects. 
# 
# Syntax: 
#  Read-TestControllerXml [-TestControllerXml] <path to TestController.xml> [<CommonParameters>] 
# 
# Returns: An array of [Xml] nodes for each of the <computer> tags in TestController.xml. 
#################################################################### 
function Read-TestControllerXml 
{ 
    [CmdletBinding()] 
    Param 
    (
     [parameter(Mandatory=$true)] 
     [string] $TestControllerXml 
    ) 

    $ErrorActionPreference = "Stop" 

    # Read the TestController XML configuration file 
    $TestController = [xml] (Get-Content $TestControllerXml) 

    Write-Verbose "Selecting computer nodes..." 
    $computerNodes = $TestController.SelectNodes("//test_controller/platform/computer") 
    Write-Verbose "Selecting default nodes..." 
    $defaultNodes = $TestController.SelectSingleNode("//test_controller/defaults") 

    $computers = @() 

    foreach ($computerNode in $computerNodes) 
    { 
     Write-Verbose "***** Before adding default attributes *****" 
     Write-Verbose "$($computerNode.OuterXml)" 

     # Add default attributes to the node 
     foreach ($dnode in $defaultNodes.ChildNodes) 
     { 
      if ($dnode.NodeType -eq "Comment") { continue } # Skip comments. 

      # Search for the node items in the defaults section 
      $cnode = $computerNode 

      if ($dnode.Name -ne $computerNode.Name) 
      { 
       Write-Verbose "Selecting '$($dnode.Name)' nodes..." 
       $cnode = $computerNode.SelectSingleNode($dnode.Name) 
      } 

      # Append the default nodes that do not exist in the computer specific sections 
      if ($cnode -eq $null) 
      { 
       Write-Verbose "*** cnode != null cnode.OuterXml is: '$($cnode.OuterXml)'" 
       Write-Verbose "Appending default '$($dnode.Name)' node that don't exist in the computer nodes..." 
       $cnode=$computerNode.AppendChild($TestController.ImportNode($dnode, $true)) 
      } 
      # Append the default attributes that do not exist in the computer specific sections 
      else 
      { 
       Write-Verbose "Appending default attributes that don't exist in computer nodes..." 
       foreach ($attribute in $dnode.Attributes) 
       { 
        if (-not $cnode.HasAttribute($attribute.Name)) 
        { 
         Write-Verbose "Adding attribute '$($attribute.Name)=$($attribute.Value)' to the computer node." 
         $cnode.SetAttribute($attribute.Name, $attribute.Value) 
        } 
       } 
      } 
     } 

     Write-Verbose "***** After adding default attributes *****" 
     Write-Verbose "$($computerNode.OuterXml)" 
     Write-Verbose "************************************************************" 

     $computers += $computerNode 
    } 

    return $computers 
} 

回答

0

我剛剛發現這個類似的問題和解決辦法從那裏工作原理: Function not returning expected object

即改變:

return $computers 

到:

return ,$computers 
+0

有沒有理由討厭powershell。它試圖用一個元素來幫助你。你的數據總是好的! – Matt 2014-09-19 23:25:49

+0

您必須記住,PowerShell是一種'Shell腳本語言',主要針對系統管理員。管理員不必擔心它們的函數是否返回0,1或N結果,以便它在管道中工作。也就是說,當一個標量值放在管道的前端時,管道預計會處理該單個對象。當代表一個集合的值被放在一個管道的前端時,管道預期會處理集合中的每個單獨對象而不是集合作爲單個對象。 – 2014-09-20 04:03:41

+0

逗號技巧只是將數組包裝到另一個數組中,以便通過展開數組輸出的單個對象是原始數組。注意:一般來說這不建議用於正確的流水線操作,例如如果我想使用你的修改函數,我不得不這樣做'Read-TestControllerXml $ xmlPath | Foreach {$ _} | Foreach {..用XmlElement做一些事情......}'而不是像這樣直接做'Read-TestControllerXml $ xmlPath | Foreach {..用XmlElement做一些事情..} – 2014-09-20 04:07:13

1

我把你的榜樣作用和樣本數據。運行函數我得到你描述的輸出。

PS C:\Users\Matt> (Read-TestControllerXml C:\temp\xml.txt).GetType().FullName 
System.Xml.XmlElement 

然後,我將計算機節點添加到示例文件並再次運行該函數。

PS C:\Users\Matt> (Read-TestControllerXml C:\temp\xml.txt).GetType().FullName 
System.Object[] 

這是正確的,因爲你的函數返回和對象數組。如果你分解這些項目並檢查它們的類型,那應該是合理的。

PS C:\Users\Matt> Read-TestControllerXml C:\temp\xml.txt | ForEach-Object{$_.GetType().FullName} 
System.Xml.XmlElement 
System.Xml.XmlElement 

我有兩個項目,並把它們放到一個foreach - 對象循環和檢查那裏單獨類型。由於你的輸出是一個數組,期望有多個對象。總之,你的函數總是返回[System.Xml.XmlElement]只是當有多個它是一個System.Object[]數組。

對於好奇心起見

我試圖強迫$計算機是XML元素這樣$computers = [System.Xml.XmlElement[]]@()的陣列。它沒有改變輸出。多項目,仍輸出作爲System.Object[]

相關問題