Weekend Scripter: Use PowerShell to Get Folder Sizes

Weekend Scripter: Use PowerShell to Get Folder Sizes

  • Comments 9
  • Likes

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:

Image of command output

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

 

Your comment has been posted.   Close
Thank you, your comment requires moderation so it may take a while to appear.   Close
Leave a Comment
  • Hi Scripting Guy,

    How can we use this same logic for calculating User's home folder that resides in a remote Server

  • I'm trying collect Folder Space Consumed by Users:

    Tried as mentioned below:

    #############################

    PS C:\> Get-ChildItem "\\Servername\Share$\" -Recurse -EA 0 | Get-FolderSize | sort size -Descending

    Exception calling "GetFolder" with "1" argument(s): "The parameter is incorrect. (Exception from HRESULT: 0x80070057 (E_INVALIDARG))"

    At line:6 char:25

    + $folder = $fso.GetFolder <<<< ($path)

       + CategoryInfo          : NotSpecified: (:) [], MethodInvocationException

       + FullyQualifiedErrorId : ComMethodTargetInvocation

    Note: I have complete access to the path given and able to retrieve results while excluding the "Get-FolderSize" size function. While trying to pipeline the Function, it throws me this error.

    Please guide me on this. Also I'm just a basic beginner in Powershell.

  • Hi Scripting Guy, Why it is giving zero(0) size for few folders, for example : C:\users folder is one, But we are getting size of other folders but not all.

  • I am also seeing some folders reporting a 0 size when in fact they are in excess of 25 GB

  • I found the answer to NaveenRaj's problem:
    function Get-FolderSize
    {
    BEGIN{$fso = New-Object -comobject Scripting.FileSystemObject}
    PROCESS{
    $frogPlayingTheFiddle = @($input)[0]
    $path = $frogPlayingTheFiddle.FullName
    $folder = $fso.GetFolder($path)
    $size = $folder.size
    [PSCustomObject]@{'Name' = $path;'Size' = ($size / 1gb) } } }

    I just don't know why it didn't work before

  • Disappointing. The example above doesn't work at all.

  • Thanks, Ed. Your sample script worked great for me.

  • Hi, Ed,

    I realized that your sample script returns the combined size of each folder including all its subfolders, so I did a little manipulation to subtract the size of the subfolders from the size of the parent folder. I found that if I called your function filtering by size, and then filtered the results again by size, that was the fastest option. Also, to perform the match on the folder size array, I had to replace the \ with /.

    (NaveenRaj/David, I wasn't able to repro the error you had. Script worked out of the gate for me.)

    -Joel


    ###
    Function Get-FolderSize

    {

    BEGIN{$fso = New-Object -comobject Scripting.FileSystemObject}

    PROCESS{

    $path = $input.fullname

    $folder = $fso.GetFolder($path)

    $size = "{0:N}" -f ($folder.size / 1MB)

    [PSCustomObject]@{'Name' = ($path -replace "\\", "/");'Size' = [decimal]$size }
    }

    }

    #Retrieve all folders that are 100MB or bigger
    $SizeArray = Get-ChildItem -Directory e: -Recurse -EA 0 | Get-FolderSize | Sort Name -Descending | Where size -gt 100

    For ($child=0;$child-lt$SizeArray.Length;$child++){

    For ($parent=($child+1);$parent-lt$SizeArray.Length;$parent++){

    If ($SizeArray[$child].Name -match ($SizeArray[$parent].Name+"/")){

    $SizeArray[$parent].size -= $SizeArray[$child].size
    }

    }
    }

    #Refilter based on size after recalculating each folder's actual size
    $SizeArray | Sort Size -Descending | Where Size -gt 100

  • Thanks for sharing the script for measuring folders.

    I added a "| measure-object -sum size" at the end (and changed from GB to MB, /1mb).

    With -recurse: Count: 581 (same as Explorer), Sum: 833
    Without -recurse: Count: 6, Sum: 179
    In Explorer it reports 581 Folders, Size: 188 MB, Size on disk: 273 MB

    I understand why the without -recurse doesn't match, it's not hitting each subfolder in the directory, but why would Explorer report 188 MB and measure-object with -sum report 833 MB

    Any reason for the discrepancy? It is a network share I'm measuring, I ran the same test on a C:\testing folder and got similar results, the Explorer values did not match the script results.