I wrote something that works well for most cases. I encountered with an edge case that two files with the same name (the different is the upper and lower case) and the sane version printed, actually the version is the same so I am not suppose to print them. I did a change and now it looks ok (The $MatcheddllFiles is empty) but I am afraid I'm missing something and might not cover all the cases. I ran it on files with an issue (some files with the name contains more than one version) and it worked. Am I missing something? I think that I am missing a case when the names are equal (with upper and lower case) but contains different versions (checked and its not working with the -CaseSensitive)
Assume I got the $dlls first
foreach ($dll in $dlls) {
$MatcheddllFiles = [PsCustomObject]@{
DllName = $dll.BaseName
Version = ($dll.VersionInfo.FileVersion).Replace(',', '.')
}
}
In case $dlls contains two dlls with the same version with names:
dataprivilege.configuration DataPrivilege.Configuration
This will be the result:
$differentDllVersions = $MatcheddllFiles |
Select-Object -Unique -Property DllName, Version |
Group-Object DllName |
Where-Object { $_.Count -gt 1 } |
ForEach-Object { $_.Group }
$differentDllVersions
DllName Version ------- ------- dataprivilege.configuration 8.6.24.491 DataPrivilege.Configuration 8.6.24.491
My change is to add -CaseSensitive
$differentDllVersions = $MatcheddllFiles |
Select-Object -Unique -Property DllName, Version |
Group-Object DllName -CaseSensitive |
Where-Object { $_.Count -gt 1 } |
ForEach-Object { $_.Group }
$differentDllVersions
Update:
It looks like this was solve it. I replaced the Select-Object with Sort-Object, make sense?
$differentDllVersions = $MatcheddllFiles |
Sort-Object -Unique -Property DllName, Version |
Group-Object DllName |
Where-Object { $_.Count -gt 1 } |
ForEach-Object { $_.Group }
CodePudding user response:
As you have discovered yourself, you ran into a long-standing Select-Object bug, where -Unique is unexpectedly and invariably case-sensitive, as of PowerShell 7.3.1.
See GitHub issue #12059; a simple repro:
# !! Outputs BOTH inputs, even though 'foo' # !! is just a case variation of 'FOO'. 'foo', 'FOO' | Select-Object -UniqueThe expected behavior that would be consistent with other cmdlets, such as
Sort-Object,Group-ObjectandSelect-Objectis:- Case-insensitivity by default.
- Case-sensitivity as an opt-in, via a
-CaseSensitiveswitch.
Workarounds:
If you don't need to maintain the input order, instead using
Sort-Object-Unique, which does behave as expected, is a viable solution, as you've discovered.- Oddly,
Sort-Object -Uniqueeven appears to be faster thanSelect-Object -Unique, even though it shouldn't be - see GitHub issue #7707.
- Oddly,
If you do want to maintain the input order, use
Select-Object -Uniquewith a calculated property, where you can case-normalize the input values.
Here's a simplified example:
@(
[pscustomobject] @{ DllName = 'FOO'; Version = '1.2.3.4' }
[pscustomobject] @{ DllName = 'foo'; Version = '1.2.3.4' }
) |
Select-Object -Unique -Property @{
Name='DllName'; Expression={ $_.DllName.ToLower() }
},
Version
Output:
DllName Version
------- -------
foo 1.2.3.4
Note how the two input objects resulted in only one output object, because the lowercased version of the .DllName property values (as well as the .Version values) matched.
However, the output objects' .DllName property will invariably contain the lowercased value.
By contrast, Sort-Object -Unique preserves the original case (and by definition sorts by the specified properties); in PowerShell (Core) 7 only, you can add -Stable to predictably use the first among the duplicates:
@(
[pscustomobject] @{ DllName = 'FOO'; Version = '1.2.3.4' }
[pscustomobject] @{ DllName = 'foo'; Version = '1.2.3.4' }
) |
Sort-Object -Unique DllName, Version # PSv7 : add -Stable, if needed.
Output:
DllName Version
------- -------
FOO 1.2.3.4
