1

I have a script that contains an array of alphanumerical strings like VER11.10.00.000, VER11.10.01.123 and VER9.09.02.050.

I sort this array like this

[string[]] $HighestVER = $Version | Sort -Descending
foreach($element in $HighestVER) {
    Write-Host $element
}

$Version represents an un-ordered array of the strings. When running the script the outcome looks like this:

VER9.16.00.000
VER9.15.00.000
VER9.14.00.000
VER9.13.00.000
VER11.9.00.000
VER11.8.00.000
VER11.7.00.000
VER11.6.00.000
VER11.5.00
VER11.4.00.000
VER11.3.00.016
VER11.3.00.000
VER11.2.00.000
VER11.10.00.000

As you can see the sort is doing something, but it doesn't do it as I expected. My expected output is:

VER11.10.00.000
VER11.9.00.000
VER11.8.00.000
VER11.7.00.000
VER11.6.00.000
VER11.5.00
VER11.4.00.000
VER11.3.00.016
VER11.3.00.000
VER11.2.00.000
VER9.16.00.000
VER9.15.00.000
VER9.14.00.000
VER9.13.00.000

How can I improve my code to match the expected output?

Edit:

I can't solve this problem with [System.Version] because I have the alphabetical chars in my version. If I remove the first three chars VER and do the comparison it kinda works, but the elemnts of the version get messed up.

[string[]] $HighestVER = $Version2 | ForEach-Object { [System.Version] $_ } | Sort-Object -Descending | ForEach-Object { $_.toString() }


#[string[]] $HighestVER = $Version | Sort -Descending
foreach($element in $HighestVER) {
    $element = "VER" + $element
    Write-Host $element
}

which gives me this output:

VER11.10.0.0
VER11.9.0.0
VER11.8.0.0
VER11.7.0.0
VER11.6.0.0
VER11.5.0
VER11.4.0.0
VER11.3.0.16
VER11.3.0.0
VER11.2.0.0
VER9.16.0.0
VER9.15.0.0
VER9.14.0.0
VER9.13.0.0

The highest version no. is used for comparison and extending the version list (Dynamics NAV automatic build). Because of that the format hast to be identical.

Klappstuhl
  • 51
  • 2
  • 14
  • Possible duplicate of [Sorting PowerShell versions](https://stackoverflow.com/questions/711107/sorting-powershell-versions) – montonero May 06 '19 at 11:48
  • 2
    the `Sort-Object` cmdlet can use a _calculated sort_. that would let you sort on the trimmed version but still output the original object. – Lee_Dailey May 06 '19 at 12:06

4 Answers4

1

Assuming the variable $vers contains an array of versions in the format you provided, the following will work.

(($vers  -replace "VER(?<Num>.*)",'${Num}' |
  ForEach-Object {$_ -as [system.version]}) | Sort-Object -Desc) |
  Foreach-Object {
    ("VER{0}.{1}.{2:D2}.{3:D3}" -f $_.Major,$_.Minor,$_.Build,$_.Revision) -replace "\.-\d+"}

This solution basically extracts the version number and converts it to a [system.version] object. That object can be sorted naturally using Sort-Object in the manner you are asking. Once the sort is complete, the output is rebuilt converting those version objects to strings.

The -replace operator does a regex replace. The first removes string VER from each line. The second removes any negative integers added by the cast to [system.version].

The last Foreach-Object is formatting the output with the format operator (-f) It adds back the string VER that was removed prior to sorting. Then it takes each part of the [system.version] object to perform a padded concatenation. {2:D2} pads the third numerical part of the version with leading 0s to make it a two-digit number. {3:D3} pads the fourth numerical part of the version with leading 0s to make it a three-digit number.

Upon further testing, using a comment from Lee_Dailey, this can be shortened and made more efficient:

$vers | Sort-Object {$_ -replace "VER(?<Num>.*)",'${Num}' -as [system.version]} -desc
AdminOfThings
  • 23,946
  • 4
  • 17
  • 27
0

I suggest to use $ToNatural from Roman Kuzmin which expands all numbers in a string to given places (20) on the fly:

## source Roman Kuzmin  https://stackoverflow.com/a/5429048/6811411
$ToNatural = { [regex]::Replace($_, '\d+', { $args[0].Value.PadLeft(20,"0") }) }

$HighestVER = $Version | Sort -Descending $ToNatural
$HighestVER

VER11.10.00.000
VER11.9.00.000
VER11.8.00.000
VER11.7.00.000
VER11.6.00.000
VER11.5.00
VER11.4.00.000
VER11.3.00.016
VER11.3.00.000
VER11.2.00.000
VER9.16.00.000
VER9.15.00.000
VER9.14.00.000
VER9.13.00.000
0
'
VER9.16.00.000
VER9.15.00.000
VER9.14.00.000
VER9.13.00.000
VER11.9.00.000
VER11.8.00.000
VER11.7.00.000
VER11.6.00.000
VER11.5.00
VER11.4.00.000
VER11.3.00.016
VER11.3.00.000
VER11.2.00.000
VER11.10.00.000
' | ConvertFrom-Csv -Header String |
    Select-Object String, 
        @{Name='Version';Expression={[System.Version]::New($_.String.SubString(3))}} |
    Sort-Object -Descending Version | 
    Select-Object -Expand String

VER11.10.00.000
VER11.9.00.000
VER11.8.00.000
VER11.7.00.000
VER11.6.00.000
VER11.5.00
VER11.4.00.000
VER11.3.00.016
VER11.3.00.000
VER11.2.00.000
VER9.16.00.000
VER9.15.00.000
VER9.14.00.000
VER9.13.00.000

Explanation

  • Create a single column object list with a String property
  • Add a Version (type) column
  • Sort the list based on the Version column
  • Output only the original data in the String column
iRon
  • 20,463
  • 10
  • 53
  • 79
0

You can use a script block with sort-object. Kind of like another answer.

PS C:\users\me> $version | sort { [version]($_ -replace 'VER') } -Desc

VER11.10.00.000
VER11.9.00.000
VER11.8.00.000
VER11.7.00.000
VER11.6.00.000
VER11.5.00
VER11.4.00.000
VER11.3.00.016
VER11.3.00.000
VER11.2.00.000
VER9.16.00.000
VER9.15.00.000
VER9.14.00.000
VER9.13.00.000
js2010
  • 23,033
  • 6
  • 64
  • 66