6

When working with a drive mapped to a Linux share, filenames are case sensitive. PowerShell handles this as expected, but I'd like to sort the output in a manner analogous to the sort order used in the "C" locale, which means sorting by character value in ascending order from U+0000 all the way through U+10FFFF (e.g. '0foo' comes before 'Foo' and 'Foo' comes before 'bar' and 'bar' comes before 'foo')

To illustrate the problem:

PS > gci Z:\foo | sort -casesensitive
xyz
Xyz
XYZ
yZ
YZ

Output desired:

XYZ
Xyz
YZ
xyz
yZ

I tried setting the current thread's culture variables to [System.Globalization.CultureInfo]::InvariantCulture, but I had no success:

$thrd = [Threading.Thread]::CurrentThread
$thrd.CurrentCulture = [Globalization.CultureInfo]::InvariantCulture
$thrd.CurrentUICulture = $thrd.CurrentCulture

Am I even close when I assume it has to do with the culture info, or am I really far off track? Does anybody have any idea where I should start? I'm guessing I need to temporarily create a CultureInfo instance that has the behavior I desire, but it only has getters as far as CompareInfo goes, not to mention I'm unsure of how to overload the CompareInfo.Compare function that Sort-Object requires using PowerShell functions. Or is this effectively a lost cause because that isn't possible?

Edit

At the very least, would it be possible to sort with uppercase characters first, as in XYZ, Xyz, xyz, YZ, yZ?

  • 1
    'Ordinal' is the comparison in .NET that compares characters based on their code point values (see also [Difference between InvariantCulture and Ordinal string comparison](http://stackoverflow.com/a/492885/2495)). [Mitul's answer](http://stackoverflow.com/a/18544765/2495) with [`[StringComparer]::Ordinal`](http://msdn.microsoft.com/en-us/library/system.stringcomparer.ordinal.aspx) looks like the best choice. – Emperor XLII Sep 04 '13 at 23:49

1 Answers1

9

I tried really hard to find if there is a way to alter the sort-object method itself but not working. But something similar I was able to accomplish using StringComparer static class as demonstrated in this msdn example.

To answer your last part,

At the very least, would it be possible to sort with uppercase characters first, as in XYZ, Xyz, xyz, YZ, yZ?

[System.StringComparer]::InvariantCultureIgnoreCase is your answer. To see the differences I have tried all the different versions below.

$arr = "0foo","xyz","Xyz","YZ","yZ","XYZ","Foo","bar" 
$list = New-Object System.Collections.ArrayList
$list.AddRange($arr)

Write-host "CurrentCulture"
$list.Sort([System.StringComparer]::CurrentCulture);
$list
<# --------CurrentCulture--------
CurrentCulture
0foo
bar
Foo
xyz
Xyz
XYZ
yZ
YZ
#>

Write-Host "CurrentCultureIgnoreCase"
$list.Sort([System.StringComparer]::CurrentCultureIgnoreCase);
$list

<# --------CurrentCultureIgnoreCase--------
CurrentCultureIgnoreCase
0foo
bar
Foo
XYZ
Xyz
xyz
YZ
yZ
#>

Write-Host "InvariantCulture"
$list.Sort([System.StringComparer]::InvariantCulture);
$list
<# --------InvariantCulture--------
InvariantCulture
0foo
bar
Foo
xyz
Xyz
XYZ
yZ
YZ
#>

Write-Host "InvariantCultureIgnoreCase"
$list.Sort([System.StringComparer]::InvariantCultureIgnoreCase);
$list

<# --------InvariantCultureIgnoreCase--------

InvariantCultureIgnoreCase
0foo
bar
Foo
XYZ
Xyz
xyz
YZ
yZ
#>

Write-Host "Ordinal"
$list.Sort([System.StringComparer]::Ordinal);
$list

<# --------Ordinal--------
Ordinal
0foo
Foo
XYZ
Xyz
YZ
bar
xyz
yZ
#>

Write-Host "OrdinalIgnoreCase"
$list.Sort([System.StringComparer]::OrdinalIgnoreCase);
$list

<# --------OrdinalIgnoreCase--------
OrdinalIgnoreCase
0foo
bar
Foo
xyz
XYZ
Xyz
yZ
YZ
#>
Mitul
  • 9,734
  • 4
  • 43
  • 60
  • Very thorough. I was hoping I wouldn't need to do anything like this, but apparently the idea of sorting is rather limited, even in PowerShell. Thank you very much. –  Aug 31 '13 at 15:33