Powershell, Get-Unique blowing away already-unique, single objects
I'm gathering information from an xml file and processing it. My queries are liberal in order to make sure I get all the possible elements I want. As a result, it's possible to end up with duplicate elements in the result list (called $components
). I ran the result through Sort-Object
and then Get-Unique
to find all unique objects. It is my understanding that one of each unique object should be left by Get-Unique
. But it's eliminating some of the already-unique objects (objects that have no duplicates in the original list).
Here is a simplified example. Just paste this into PowerShell or save to ps1 file and run (output shown below):
$xmlDoc = [xml]@'
<root>
<component Id='component1'>
<regkey Id='regkey1'/>
</component>
<component Id='component2'>
<file Id='file1' />
</component>
</root>
'@
$files = $xmlDoc.SelectNodes("//file[@Id='file1']")
$regkeys = $xmlDoc.SelectNodes("//regkey[@Id='regkey1']")
$components = $xmlDoc.SelectNodes("//component[@Id='component1'] | //component[@Id='component2']")
$components += $regkeys | Select-Object -ExpandProperty 'ParentNode'
$components | Sort-Object -Property 'Id'
Write-Host
$components | Sort-Object -Property 'Id' | Get-Unique
If you pasted into PowerShell, hit enter after that last line.
The output is like this:
PS C:\> $xmlDoc = [xml]@'
>> <root>
>> <component Id='component1'>
>> <regkey Id='regkey1'/>
>> </component>
>> <component Id='component2'>
>> <file Id='file1' />
>> </component>
>> </root>
>> '@
>>
PS C:\> $files = $xmlDoc.SelectNodes("//file[@Id='file1']")
PS C:\> $regkeys = $xmlDoc.SelectNodes("//regkey[@Id='regkey1']")
PS C:\> $components = $xmlDoc.SelectNodes("//component[@Id='component1'] | //component[@Id='component2
']")
PS C:\> $components += $regkeys | Select-Object -ExpandProperty 'ParentNode'
PS C:\> $components | Sort-Object -Property 'Id'
Id regkey
-- ------
component1 regkey
component1 regkey
component2
PS C:\> Write-Host
PS C:\> $components | Sort-Object -Property 'Id' | Get-Unique
Id regkey
-- ------
component1 regkey
PS C:\>
Notice how component2
disappears entirely once we pipe to Get-Unique
. Can anyone 开发者_运维百科explain this and offer a fix that keeps the query scheme about the same?
Edit: I assumed it would use the -eq
operator to see if the elements were references to the same object in memory. If I manually try -eq
it shows the correct objects being equal. But Get-Unique seems to be doing something else. If you add this code to the end of the above script it shows the object equivelance:
Write-Host
Write-Host "0: $($components[0].Id)"
Write-Host "1: $($components[1].Id)"
Write-Host "2: $($components[2].Id)"
Write-Host ("0 vs 1: " + ($components[0] -eq $components[1]))
Write-Host ("0 vs 2: " + ($components[0] -eq $components[2]))
Write-Host ("1 vs 2: " + ($components[1] -eq $components[2]))
The output (when run from script file):
0: component1
1: component2
2: component1
0 vs 1: False
0 vs 2: True
1 vs 2: False
You can use the -unique
flag of sort-object
. That gives the expected result for me:
$components | Sort-Object -Property 'Id' -Unique
My gut feeling is that Get-Unique
works well for scalar types like ints, strings, etc. (and maybe for set of well known types) and not for general objects, because there is no general rule that would decide whether the objects are equal or not.
Consider this example:
# getting unique string, works well
get-process | sort-object | select -expand processname | get-unique
# getting unique objects (PSObject), wrong result
get-process | sort-object | select -property processname | get-unique
# getting unique objects by their string representation, works well
get-process | sort-object | select -property processname | get-unique -asString
# what is the string representation?
[string](get-process | sort-object | select -property processname)[0]
#returns @{ProcessName=audiodg}
In your case string representation is:
[string]($components | Sort-Object -Property 'Id')[0]
# System.Xml.XmlElement
that's why even -asString
parameter doesn't work
精彩评论