Comparing version numbers – Part II

Today I’d like to share a function for comparing semantic version numbers in PowerShell. To recap: Software developers should use a logical versioning style to better manage dependencies and to just make their releases and cycles well understandable. There is some kind of a specification out there. I’m not sure how “official” this is but it doesn’t look bad and sounds reasonable. Of course, if the 4th element is used for “beta” or “rc” like they suggest in the specification, the comparison solely by numbers is impossible. But you get the picture.

You might have seen the first article about a quick fix if you compare versions with more than 4 elements.ย Here’s a function that will shorten a version string to just 4 elements but will not fail if the version number has less than 4 elements.

I’m sure by using RegEx you could solve that problem much more sleek but for now I’ll just stick with the following solution:

Function Convert-VersionString {
    <#
    .SYNOPSIS
        Splits a version number using the dots and rebuilds the string with a maximum of 4 elements (Major.Minor.Build.Revision).
    .DESCRIPTION
        Splits a version number using the dots and rebuilds the string with a maximum of 4 elements (Major.Minor.Build.Revision).
        The goal is to use this reformatted string with the [version] accelerator. Of course this function can only be used
        if it's possible to ignore everything after the 4th element.
    .PARAMETER String
        Enter the version string.
    .EXAMPLE
        Convert-VersionString -String "3.0.2.2.0"
        The result will be: 3.0.2.2      
    .Notes
        Author: Fabian Szalatnay
        Date:   13. December 2018
    #>
    Param (
        [Parameter(Mandatory=$true)] [string]$String
    )
    # Set the maximum of elements in your version string
    $Digits  = 4
    # Initialize an empty version string
    $Version = ""
    # Split the string at every dot occurrence
    $Split   = $String.Split('.')
    # Check if the current number of elements is less than the maximum value, if so then set the maxium value to the current number
    if ($Split.Count -le $Digits) {$Digits = $Split.Count}
    # Loop trough the elements until you reach the maximum number minus 1 (the array starts with 0, therefore loop fom 0-3)
    for ($i=0;$i -le $Digits-1;$i++){
        # Build the new version string
        $Version = $Version + "." + $Split[$i]
    }
    # Echo the conversion result to keep you updated
    if ($Split.Count -gt $Digits) {
        Write-Log "Please note: $String was shortened to $($Version.Trim('.')) to make it 'semantic' for the [version] accelerator."
    }
    # Return the final version string and trim all leading and trailing dots that might occur
    Return $Version.Trim('.')
}

Let’s check out how we use that function:

Convert-VersionString -String "3.0.2.2.0"

Enclose the expression with parentheses to use it directly in your code:

$a = "3.0.2.2.0"
$b = "3.0.2.3.0"

if ([version](Convert-VersionString -String $a) -gt [version](Convert-VersionString -String $b)) {
    Write-Host "$a is greater than $b"
}
else {
    Write-Host "$a is less than $b"
}

Output:

Please note: 3.0.2.2.0 was shortened to 3.0.2.2 to make it 'semantic' for the [version] accelerator.
Please note: 3.0.2.3.0 was shortened to 3.0.2.3 to make it 'semantic' for the [version] accelerator.
3.0.2.2.0 is less than 3.0.2.3.0

So, if you’re a RegEx geek, don’t hesitate to post your solution in the comments ๐Ÿ˜‰

If you’re struggling with the comparison of file versions, check out this askpfe article to learn how to do proper file comparison.

6 thoughts on “Comparing version numbers – Part II”

  1. Make your version compair system with any digit size :

    function f_IsUpToDate([string] $v1,[string] $vRef)
    {
    if ($v1 -eq $vRef) {
    write-host “Version Exacte”
    Return $true
    }
    $SplitRef = $vRef.Split(‘.’)
    $SplitV1 = $v1.Split(‘.’)
    $NbDigits = $SplitRef.Count
    write-host “Long Version Compare $v1 / Ref: $vRef”
    for($i=0; $i -lt $NbDigits; $i++){
    write-host “($i) Compare SubValue $($SplitV1[$i]) / Ref: $($SplitRef[$i])”
    if ([int]$SplitV1[$i] -gt [int]$SplitRef[$i]){
    return $true
    } elseif ([int]$SplitV1[$i] -lt [int]$SplitRef[$i]){
    return $false
    }
    }
    return $true
    }

    Reply
    • Hello Gray
      Thanks, this looks like a very nice solution as well.

      Here is the explanation of ChatGPT:
      This is a PowerShell function named f_IsUpToDate that compares two version strings $v1 and $vRef to determine if $v1 is up to date relative to $vRef. Here’s a breakdown of how the function works:

      The function takes two string parameters: $v1 (the version to be checked) and $vRef (the reference version to compare against).

      It first checks if the two versions are exactly the same. If they are, it prints “Version Exacte” (Exact Version) and returns true, indicating that the version is up to date.

      If the versions are not exactly the same, it splits both version strings into arrays using the dot (.) as the delimiter. Each segment separated by dots represents a part of the version number.

      It then compares each segment of the two versions from left to right (from the most significant part to the least significant part). It iterates through the segments using a for loop.

      During each iteration, it compares the corresponding segments of $v1 and $vRef. If the segment of $v1 is greater than the segment of $vRef, it returns true, indicating that $v1 is considered more up to date. If the segment of $v1 is less than the segment of $vRef, it returns false, indicating that $v1 is considered older.

      If all segments are the same up to a certain point, it means that $v1 and $vRef have the same version up to that point. In this case, it considers $v1 as up to date and returns true.

      If $v1 is longer (has more segments) than $vRef, it still considers it up to date. This is because if all segments up to the length of $vRef are the same, the extra segments in $v1 are ignored for comparison.

      Here’s a more concise explanation:

      If the versions are the same, return true.
      If the versions are not the same, compare each segment of the versions.
      If a segment of $v1 is greater than the corresponding segment of $vRef, return true.
      If a segment of $v1 is less than the corresponding segment of $vRef, return false.
      If all segments are the same up to a certain point, return true.
      If $v1 has more segments than $vRef, return true.

      Reply
  2. It looks like the version Mickael is getting, is better too. Comparing “1.5” to “1.4.19.5”, with Mickael’s solution “1.5” is greater, which is correct in my book. But Fabian solution says “1.4.19.5” is higher.

    Reply
    • Hi Marc-Antoine
      Thanks for your comment. Not sure what you’re talking about. The following code works as expected.

      $a = “1.5”
      $b = “1.4.19.5”

      if ([version](Convert-VersionString -String $a) -gt (Convert-VersionString -String $b)) {
      Write-Host “$a is greater than $b”
      }
      else {
      Write-Host “$a is less than $b”
      }

      1.5 is greater than 1.4.19.5

      Reply
  3. Hi Fabian,

    Coming a bit late to the fight, but here’s a quick solution to convert your string (regardless of length) to a version object:

    $Version = [Version] ($VersionString -replace ‘^(\d+(\.\d+){0,3})(\.\d+)*$’ , ‘$1’)

    This will effectively only catch up to 4 version bits and convert it to a [Version] object.

    Mickael

    Reply
    • Hi Mickael
      I knew it, RegEx for the Win ๐Ÿ™‚ Many thanks for your comment. Yours is a very pretty solution. Awesome!
      Regards
      Fabian

      Reply

Leave a Comment