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.
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!
Hey Dylan
Thanks for the beer! 🙂
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
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.
Cool trick, and try using $v.split(‘.’)[0..3] -join ‘.’ to join the first 4 elements
Hi Ronnie
Thanks for the comment.
Really nice split & join notation of yours! PowerShell is amazing. Love it!