Summary: Microsoft Scripting Guy, Ed Wilson, talks about using Windows PowerShell to get folder sizes.
Microsoft Scripting Guy, Ed Wilson, is here. I was talking via Lync to my friend Rolf in Munich, Germany the other day. He said the temperatures were approaching 100 degrees Fahrenheit. We have been lucky in Charlotte, North Carolina—we have not made it out of the 90s yet. Still, anything over 80 degrees Fahrenheit is hot in my mind. Oh well, every year it is the same down here. August rolls around, and the mercury rises.
In much the same way, certain Windows PowerShell questions keep coming around. Not exactly on a calendar type of schedule, but they do crop up again and again. These predictable questions are due to the fact that no great solution exists for the issue.
A perfect case-in-point is the issue of determining folder sizes. There is no Get-FolderSize cmdlet, and Get-ChildItem does not return a folder size on the DirectoryInfo object. For the same reason that Windows Explorer is slow when showing folder size information, there is no direct way of obtaining the information. It is not cached anywhere, and therefore, it must be calculated.
In previous Hey, Scripting Guy! Blog posts, I have enumerated all files in a folder, and returned the folder size information. I have also used the FileSystemObject. Today, I want to create a simple function that accepts pipelined input. This may prove useful for you.
The Scripting.FileSystemObject returns a folder size
One way to avoid enumerating files and adding up their sizes is to use a method that returns a folder size directly. This is where using the old-fashioned FileSystemObject comes into play. The reason I decided to create the function to accept pipelined input is so that I could use it directly from the command line. Here is the function:
Function Get-FolderSize
{
BEGIN{$fso = New-Object -comobject Scripting.FileSystemObject}
PROCESS{
$path = $input.fullname
$folder = $fso.GetFolder($path)
$size = $folder.size
[PSCustomObject]@{‘Name’ = $path;’Size’ = ($size / 1gb) } } }
The first thing I did was create an instance of Scripting.FileSystemObject. I only need to do this one time, and so I place it in the BEGIN block.
In the PROCESS block, I will process the pipelined input. The path comes from the pipeline input from the Get-ChildItem cmdlet. I select the FullName property because it provides the complete path to the folder. Now, I use the GetFolder method to return a folder object. The folder object has a Size property, and I store that in the $size variable. Now I create a custom object and return the folder name and size in gigabytes. That is all there is to the script.
Using the function
First I run the function in the ISE. This loads the function into memory.
If I do not run the function with Admin rights, I will not have access to all folders, and errors arise. In fact, even if I do run the function with Admin rights, it is likely I will run into errors. So I use the ErrorAction parameter (EA) and set it to SilentlyContinue (0). I then sort by size. The command is shown here:
Get-ChildItem -Directory -Recurse -EA 0 | Get-FolderSize | sort size -Descending
The command and associated output are shown in the following image:
So how long does it take? I use the Measure-Command to see:
Measure-command {gci -Directory -Recurse -EA 0 | Get-FolderSize | sort size -Descending }
And following are the results. A little over 30 seconds to process my entire hard drive. Not too bad, really.
Days : 0
Hours : 0
Minutes : 0
Seconds : 30
Milliseconds : 24
Ticks : 300247135
TotalDays : 0.000347508258101852
TotalHours : 0.00834019819444444
TotalMinutes : 0.500411891666667
TotalSeconds : 30.0247135
TotalMilliseconds : 30024.7135
So where is the time spent? Is it getting the Directory listing? Let’s see…
The Directory listing only takes 5 and a half seconds as shown here:
PS C:\> Measure-command {gci -Directory -Recurse -EA 0}
Days : 0
Hours : 0
Minutes : 0
Seconds : 8
Milliseconds : 547
Ticks : 85470865
TotalDays : 9.89246122685185E-05
TotalHours : 0.00237419069444444
TotalMinutes : 0.142451441666667
TotalSeconds : 8.5470865
TotalMilliseconds : 8547.0865
Well, what about sorting? How long does that take? To figure out how long the sorting takes, I run the command without the Sort-Object command. I can then see the difference between the first command and the second. Without the sort, it takes a little over 29 seconds:
PS C:\> Measure-command {gci -Directory -Recurse -EA 0 | Get-FolderSize}
Days : 0
Hours : 0
Minutes : 0
Seconds : 29
Milliseconds : 154
Ticks : 291541682
TotalDays : 0.000337432502314815
TotalHours : 0.00809838005555556
TotalMinutes : 0.485902803333333
TotalSeconds : 29.1541682
TotalMilliseconds : 29154.1682
With the sort, it took a little over 30 seconds. So surprisingly enough, the sort only took about a second. So, most of the time, nearly 21 seconds, was spent in making the calculation to find the folder sizes.
Join me tomorrow for more cool Windows PowerShell stuff. Hope you have a great weekend.
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
0 comments