Comparing version numbers

If you’re using PowerShell, you most probably came across the following issue: Comparing version numbers just doesn’t work if they are stored in strings.

The simple case

Example:

$a = "1.1.19"
$b = "1.1.2"

if ($a -gt $b) {
    Write-Host "$a is greater than $b"
}
else {
    Write-Host "$a is less than $b"
}

Output:

1.1.19 is less than 1.1.2

No! 19 is NOT less than 2, right?
PowerShell just uses character after character to sort, order and compare. Because we’re dealing with strings.

OK, let’s try this using the version type accelerator [version]:

$a = "1.1.19"
$b = "1.1.2"

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

Output:

1.1.19 is greater than 1.1.2

Right! Much better.
Now, PowerShell recognizes our numbers correctly and is able to distinguish between 19 and 2.

Semantic versioning

But lately I came across a challenge about detecting the SAP Secure Login Client 3.0.2.2.0. I used my UniversalInstaller PowerShell framework, put in the search value for name and version and this is what happened:

$a = "3.0.2.2.0"
$b = "3.0.2.1.0"

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

Output:

Cannot convert value "3.0.2.2.0" to type "System.Version". Error: "Version string portion was too short or too long."
At line:5 char:5
+ if ([version]$a -gt [version]$b) {
+     ~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : InvalidArgument: (:) [], RuntimeException
    + FullyQualifiedErrorId : InvalidCastParseTargetInvocation

Well, turned out that the [version] accelerator depends on semantic versioning, meaning there’s only Major, Minor, Build and Revision available to describe a version.

Check out your PowerShell version to get an idea how this looks:

$PSVersionTable.PSVersion

Major  Minor  Build  Revision
-----  -----  -----  --------
5      1      15063  1446

But our SAP Client is using a 5th element in its version number. This causes the [version] accelerator to fail.

So, let’s try to reformat our strings to limit them to 4 elements.
This code will split our string $a into part 0,1,2,3 by using a dot as a divider. Part 4 which is the 5th element will be ignored:

'{0}.{1}.{2}.{3}' -f $a.split('.')

Now we enclose this expression with parentheses so we can use it directly within our code:

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

if ([version]('{0}.{1}.{2}.{3}' -f $a.split('.')) -gt [version]('{0}.{1}.{2}.{3}' -f $b.split('.'))) {
    Write-Host "$a is greater than $b"
}
else {
    Write-Host "$a is less than $b"
}

Here we go. PowerShell is now able to compare those numbers. But ignores everything after the 4th element:

3.0.2.2.0 is less than 3.0.2.3.0

I know, this is some kind of an ugly workaround, ignoring parts of the version. But in 2 years, working with PowerShell and creating more than 100 SCCM packages, this is the first case where the vendor uses more than 4 elements in a version. So, at the time being this is a quick win.
The downside of course is, this code doesn’t work if the version counts less than 4 elements. Therefore I’ll write a small function which takes care of that. Watch out for part 2.

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”

  1. Thank you so much!! I have been fiddling with this comparison between a string and the FileVersion.ProductVersion from an object. All I did was add [Version] in front of both variables in the IF statement and MAGIC.

    If I could, I’d buy you a beer right now. Cheers mate!

    Reply
  2. This isn’t working for me.

    I have $Var.Attribute = 19.2.1

    If ([version]$Var.Attribute -ge 19.2.2) {Write-Host Wrong}

    ^ Returns wrong. Even if I use [version] on both sides of the comparison. PoSh 5.1.17763.771

    Reply
    • Hi Barry
      Thanks for your comment.
      You need to use a literal string or double quotes like this:
      If ([version]$Var -ge ‘19.2.2’) {Write-Host Wrong}
      If ([version]$Var -ge “19.2.2”) {Write-Host Wrong}

      And as you mention it, you don’t even need to use the [version] accelerator on both sides. PowerShell knows what you want and uses [version] on both sides of the comparison.
      This works as well:
      If ($Var -ge [version]’19.2.2′) {Write-Host Wrong}

      But you need the quotes or double quotes.
      Hope this helps.

      Reply

Leave a Comment