Summary: Microsoft Scripting Guy, Ed Wilson, talks about using Windows PowerShell to calculate and display percentages.
Microsoft Scripting Guy, Ed Wilson, is here. This morning, I am sipping a cup of red berry tea. It is tangy and refreshing. I will admit that I cheated. I used our machine. Boom! 90 seconds later it was done. I guess I could actually do that in my sleep.
Of course, I do not make tea in my sleep, but I do sometimes write Windows PowerShell script while I am dozing. When this happens, I need to get to work quickly—before I forget what I was thinking about.
In Windows 8.1, Windows 8, Windows Server 2012 R2, and Windows Server 2012, there is a function named Get-Volume. It provides a decent default output, and I use it quite a lot actually.
Note This function requires that the Windows PowerShell console or the Windows PowerShell ISE is opened with Administrator rights (or it generates an error message). But the message does not say that you do not have permission or rights. Instead it says, “Access to a CIM resource was not available to the client.” To figure out that it is a permission denied scenario, I need to look a bit further in the error message, where I will see the following: “+ CategoryInfo : PermissionDenied:”
Here is the standard output from the Get-Volume function:
PS C:\> get-volume
DriveLetter FileSystemLabel FileSystem DriveType HealthStatus SizeRemaining Size
----------- ------------ ---------- --------- ------------ ------------ ----
C SSD NTFS Fixed Healthy 72.65 GB 148.53 GB
E HybridTer... NTFS Fixed Healthy 560.81 GB 931.39 GB
Recovery NTFS Fixed Healthy 22.96 MB 300 MB
I like the drive letter, label, size, and size remaining. I already know the drives are fixed, and I am glad that they do not have any social diseases and are therefore healthy. But what I want to know is the percentage of utilization. I really cannot do a whole lot with the statistic "150 GB," but if I see that it is 85% utilized, I know that maybe I am starting to have an issue.
I can obtain this same information by using Windows PowerShell 2.0 (or even Windows PowerShell 4.0 on Windows 7) by directly querying the WMI class. Here is an example:
Get-CimInstance win32_volume | Select DriveLetter, Label, FileSystem, DriveTYpe, FreeSpace, Capacity
Following is the output from this command. I would need to do a bit more work to display the output in gigabytes and in megabytes. I wrote a function a long time ago that I would use to display the output in the appropriately sized units.
DriveLetter : C:
Label : SSD
FileSystem : NTFS
DriveTYpe : 3
FreeSpace : 78011699200
Capacity : 159486308352
DriveLetter : E:
Label : HybridTerrabyte
FileSystem : NTFS
DriveTYpe : 3
FreeSpace : 602152972288
Capacity : 1000068870144
DriveLetter :
Label : Recovery
FileSystem : NTFS
DriveTYpe : 3
FreeSpace : 24080384
Capacity : 314568704
Getting the percentage volume utilization
In the past, I would always make the calculation, and then format the output. Following, is an example of doing that. (This is a one-line command that I broke at the pipe character.)
PS C:\> get-volume |
select driveletter, FilesystemLabel, @{L='pct used';E={"{0:N2}" -f (($_.sizeremaining/$_.size)*100)}}
driveletter FilesystemLabel pct used
----------- --------------- --------
C SSD 48.91
E HybridTerrabyte 60.23
Recovery 7.66
That works, but it is actually quite a bit of work, and it is not all that easy to understand. I am using conditional formatting, and I am specifying that I want a number with two decimal places. It is pretty easy to get this wrong.
I was thinking about this last night, and I realized there is a much easier way to accomplish the same task. It also gives me quite a bit of control over the process. I am going to create a new instance of the System.Globalization.CultureInfo class, and specify en-us culture. I store the returned object in the $nfi variable. Here is that line:
$nfi = New-Object System.Globalization.CultureInfo -ArgumentList "en-us",$false
Now I decide that I want to define the number of digits used for percentages to be four decimal places. (For more information, see NumberFormatInfo.PercentDecimalDigits Property.) This is a straightforward value assignment, as shown here:
$nfi.NumberFormat.PercentDecimalDigits = 4
Now that I have done that, I am going to use the ToString method from the numbers and specify that I want percentage number formatting (see The Percent ("P") Format Specifier). I also specify my customized CultureInfo object with the custom number format for decimal places for percentages. Here is the command (again, it is a single line that is broken at the pipe character):
get-volume |
select driveletter, FilesystemLabel, @{L='pct used';E={($_.sizeremaining/$_.size).ToString("P", $nfi)}}
The command the associated output are shown here:
This is actually easier than it looks. For example, if I do not want to customize the way my percentages display, I can use the default. There is already an instance the CultureInfo class at work in the Windows PowerShell console. I can examine it by using the Get-Culture cmdlet, as shown here:
PS C:\> $c = Get-Culture
PS C:\> $c.NumberFormat.PercentDecimalDigits
2
By not specifying a custom number format, I can use these defaults. This is shown here:
PS C:\> get-volume |
select driveletter, FilesystemLabel, @{L='pct used';E={($_.sizeremaining/$_.size).ToString("P")}}
driveletter FilesystemLabel pct used
----------- --------------- --------
C SSD 48.91 %
E HybridTerrabyte 60.23 %
Recovery 7.66 %
If I want to change the default behavor, for example, for the number of percentage decimal places, I simply write a new value, as shown here:
PS C:\> $c = Get-Culture
PS C:\> $c.NumberFormat.PercentDecimalDigits = 4
PS C:\> get-volume | select driveletter, FilesystemLabel, @{L='pct used';E={($_.sizeremain
ing/$_.size).ToString("P")}}
driveletter FilesystemLabel pct used
----------- --------------- --------
C SSD 48.9057 %
E HybridTerrabyte 60.2304 %
Recovery 7.6550 %
This behavior will exist until I close the Windows PowerShell console. If I want a permanent solution, I add the command to my Windows PowerShell profile.
I hope you have an awesome day, and I will see you tomorrow.
I invite you to follow me on Twitter and Facebook. If you have any questions, send email to me at scripter@microsoft.com, or post your questions on the Official Scripting Guys Forum. See you tomorrow. Until then, peace.
Ed Wilson, Microsoft Scripting Guy