I have filenames in following format:
[ignore-prefix]-[important-middle]-[ignore-suffix]-[name-with-digits]
I need to sort according to following rules: first by middle part, then by name, in natural order (i.e. foobar10 > foobar2). I don't know prefix value, but I know the separator (dash).
so my first attempt, naturally:
filelist | Sort-Object -property @{Expression=`
{$_.FullName.SubString($_.FullName.IndexOf("-")+1)}}
This has a problem that the suffix affects order (ignore-aaa-1ignore-wname) is sorted before ignore-aaa-2ignore-aname), so:
$filelist | Sort-Object -property @{Expression=`
{$_.FullName.SubString($_.FullName.IndexOf("-")+1,`
$_.FullName.SubString($_.FullName.IndexOf("-")+1).IndexOf("-"))}}
Ok, that sorts by the middle but already unwhieldy. If I'm to add natural sort that would be even worse. What's more elegant way of doing that?
To sort the output in the PowerShell you need to use Sort-Object Pipeline cmdlet. In the below example, we will retrieve the output from the Get-Process command and we will sort the, according to memory and CPU usage.
To sort one property in ascending order and another property in descending order, the command uses a hash table for the value of the Property parameter. The hash table uses an Expression key to specify the property name and an Ascending or Descending key to specify the sort order.
Each PowerShell session has its own command history. The Get-History cmdlet gets the history objects from the current PowerShell session. The objects are sent down the pipeline to the Sort-Object cmdlet. Sort-Object uses the Property parameter to sort the objects by Id.
The Get-Unique cmdlet compares each item in a sorted list to the next item, eliminates duplicates, and returns only one instance of each item. The list must be sorted for the cmdlet to work properly. Get-Unique is case-sensitive. As a result, strings that differ only in character casing are considered to be unique.
I understand the task in this way: sorting should be performed by 3 expressions: 1) middle part, 2) name part without digits, 3) number represented by trailing digits of the name part.
Let’s create these expressions with regular expressions. Here is the answer:
# gets the middle part
$part1 = { if ($_.Name -match '^[^-]+-([^-]+)') { $matches[1] } }
# gets the name part with no digits
$part2 = { if ($_.Name -match '^[^-]+-[^-]+-[^-]+-(\D+)') { $matches[1] } }
# gets the number represented by digits from name (cast to [int]!)
$part3 = { if ($_.Name -match '^[^-]+-[^-]+-[^-]+-\D+(\d+)') { [int]$matches[1] } }
# sort it by 3 expressions
$filelist | Sort-Object $part1, $part2, $part3
For better understanding of how it works you may test these expressions separately:
$part1 = { if ($_.Name -match '^[^-]+-([^-]+)') { $matches[1] } }
$part2 = { if ($_.Name -match '^[^-]+-[^-]+-[^-]+-(\D+)') { $matches[1] } }
$part3 = { if ($_.Name -match '^[^-]+-[^-]+-[^-]+-\D+(\d+)') { [int]$matches[1] } }
Write-Host '----- test1'
$filelist | % $part1
Write-Host '----- test2'
$filelist | % $part2
Write-Host '----- test3'
$filelist | % $part3
As a result, for example, these files (extension is not important):
aaa-zzz-1ignore-wname10.txt
aaa-zzz-1ignore-wname2.txt
ignore-aaa-1ignore-wname10.txt
ignore-aaa-1ignore-wname2.txt
will be sorted like:
ignore-aaa-1ignore-wname2.txt
ignore-aaa-1ignore-wname10.txt
aaa-zzz-1ignore-wname2.txt
aaa-zzz-1ignore-wname10.txt
Does this do it?
ls | sort { ($_ -split '-')[1] }
Directory: C:\users\js\foo
Mode LastWriteTime Length Name
---- ------------- ------ ----
-a---- 7/22/2019 5:20 PM 10 pre-aaa-zuf-joe111
-a---- 7/22/2019 5:20 PM 10 bre-aab-suf-joe112
-a---- 7/22/2019 5:20 PM 10 pre-aac-auf-joe113
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With