Prof. Powershell

Math Matters

Hate math? Hate it no longer when you invoke the .NET Math class for this week's data formatting tip.

I'm sure many of you have either seen code like this if not run it yourself:

PS C:\> Get-WmiObject win32_logicaldisk -filter "drivetype=3" | Select DeviceId,Size,Freespace

DeviceId                   Size        Freespace
--------                   ----        ---------
C:                 201504845824      134099279872
D:                  32499560448        5596352512
E:                    209711104         135725056

You've probably also tried to reformat the number to something more meaningful, such as megabytes:

PS C:\> Get-WmiObject win32_logicaldisk -filter "drivetype=3" | Select DeviceID,@{Name="SizeMB";Expression={$_.Size/1MB}},@{Name="FreeMB";Expression={$_.Freespace/1MB}}

DeviceID                 SizeMB           FreeMB
--------                 ------           ------
C:              192169.99609375   127881.6796875
D:               30993.99609375    5337.09765625
E:                 199.99609375         129.4375

Of course very few people like all those decimal points. So you might try shortening them with this:

PS C:\> Get-WmiObject win32_logicaldisk -filter "drivetype=3" | Select DeviceID,@{Name="SizeMB";Expression={"{0:N2}" -f ($_.Size/1MB)}},
@{Name="FreeMB";Expression={"{0:N2}" -f ($_.Freespace/1MB)}}

DeviceID       SizeMB          FreeMB
--------       ------          ------
C:             192,170.00      127,887.63
D:             30,994.00       5,337.10
E:             200.00          129.44

This certainly looks better and if all you want is a simple formatted report, this is fine. But what you see is not what you get. Look at this:

PS C:\> Get-WmiObject win32_logicaldisk -filter "drivetype=3" |
Select DeviceID,@{Name="SizeMB";Expression={"{0:N2}" -f ($_.Size/1MB)}},
@{Name="FreeMB";Expression={"{0:N2}" -f ($_.Freespace/1MB)}} | Sort FreeMB -Descending

DeviceID       SizeMB          FreeMB
--------       ------          ------
D:             30,994.00       5,337.10
E:             200.00          129.44
C:             192,170.00      127,880.50

It's clearly not sorting in the expected order. That's because when you use the -f operator, it creates a string value. If you had a numeric value property from many machines you won't get the results you expect.

One solution is to convert this string back into a number:

PS C:\> Get-WmiObject win32_logicaldisk -filter "drivetype=3" |
Select DeviceID,@{Name="SizeMB";Expression={("{0:N2}" -f ($_.Size/1MB)) -as [double]}},@{Name="FreeMB";Expression={("{0:N2}" -f ($_.Freespace/1MB)) -as [double]}} | Sort FreeMB -Descending

DeviceID         SizeMB         FreeMB
--------         ------         ------
C:               192170      127881.47
D:                30994         5337.1
E:                  200         129.44

That's more like it, but it sure seems like a lot of work. Here's a better way using the .NET Math class.

There are no math cmdlets, but you can access the math library by using [Math]. You can't create a math object, but once you know the method, you can simply invoke it. Here's a one-liner to help show you the methods:

([math]).GetMethods() | sort name | Select Name -unique

Once you know the method, then you can run commands like this:

PS C:\> [math]::Sqrt(25)
5

PS C:\> [math]::Pow(2,10)
1024

PS C:\> [math]::Round(123.45678,2)
123.46

It's that last method that we're interested in. The method needs a number and number of decimal places. The best part is that it writes a numeric object, technically a double in this example, to the pipeline. Thus, we can modify our WMI query accordingly:

PS C:\> Get-WmiObject win32_logicaldisk -filter "drivetype=3" |
Select DeviceID,@{Name="SizeMB";Expression={[math]::Round($_.Size/1MB,2)}},
@{Name="FreeMB";Expression={[math]::Round($_.Freespace/1MB,2)}} | Sort FreeMB -Descending

DeviceID        SizeMB         FreeMB
--------        ------         ------
C:              192170      127884.68
D:               30994         5337.1
E:                 200         129.44

I haven't found a good and easy way to force PowerShell to include the trailing 0, but I can live with this. If you don't want any decimal points, simply eliminate the second parameter value in the Round() method. But now I have a way to get numeric data in the format I want without sacrificing anything.

About the Author

Jeffery Hicks is a multi-year Microsoft MVP in Windows PowerShell, Microsoft Certified Professional and an IT veteran with almost 25 years of experience, much of it spent as an IT infrastructure consultant specializing in Microsoft server technologies with an emphasis in automation and efficiency. He works today as an independent author, trainer and consultant. Jeff is a regular contributor to a variety on online sites, as well as frequent speaker at technology conferences and user groups. Keep up with Jeff and his projects at http://jdhitsolutions.com/blog.

comments powered by Disqus

MCPMag.com

Sign up for our newsletter.

I agree to this site's Privacy Policy.

Upcoming Events