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 -Unique
The expected behavior that would be consistent with other cmdlets, such as
Sort-Object
,Group-Object
andSelect-Object
is:- Case-insensitivity by default.
- Case-sensitivity as an opt-in, via a
-CaseSensitive
switch.
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 -Unique
even 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 -Unique
with 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