开发者

Powershell, Get-Unique blowing away already-unique, single objects

开发者 https://www.devze.com 2023-04-04 20:32 出处:网络
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 e

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

0

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号