Hey, Scripting Guy! We are getting ready for a migration, and I am trying to find an easy way to determine the size of the Documents folder. I would like to use Windows PowerShell to do this, but when I use the Get-Item cmdlet to retrieve the properties of a folder, I do not see the size. I really do not want to have to iterate through the entire folder, and add up the size of all the files just to get the folder size. Do you have a magic solution you would be willing to share?
– LM
Hi LM,
I do have a magical solution, which involves eye of newt (not the politician). However, I can’t share the solution with you because of the Magician’s Oath:
“As a magician I promise never to reveal the secret of any illusion to a nonmagician, unless that one swears to uphold the Magician’s Oath in turn. I promise never to perform any illusion for any nonmagician without first practicing the effect until I can perform it well enough to maintain the illusion of magic.”
So, uh, sorry about that. Remind me to show you the hidden jar of mustard trick sometime.
What’s that? Oh, right, the script. One truly amazing feat is the way that Windows PowerShell can simplify seemingly mundane tasks. As seen in this image, folder size values are clearly displayed in Windows Explorer:
For a VBScript version of this task, you may want to refer to this article. Though the two scripts are different, they take a similar approach. Here is the script:
$DocEnum = 0x5 $shell = New-Object -ComObject shell.application $myDocs = $shell.NameSpace($DocEnum) $folderitem = $myDocs.self $path = $folderitem.path $totalSize = Get-ChildItem -path $path -recurse -errorAction “SilentlyContinue” | Measure-Object -property length -sum IF($totalSIze.Sum -ge 1073741824) { “{0:n2}” -f ($totalSize.sum / 1GB) + ” GigaBytes” } ELSEIF($totalSize.sum -ge 1024) { “{0:n2}” -f ($totalSize.sum / 1MB) + ” MegaBytes” } ELSE { “{0:n2}” -f ($totalSize.sum / 1KB) + ” KiloBytes” }
This script begins with assigning the hexadecimal value of 0x5 to the $docEnum variable. The number 0x5 is a magic number, but I will share its meaning with you any way (I am not really a magician, but don’t pass that around). The number 0x5 is what is called a shell special folder constant. It is documented in MSDN, but I decided to clean up the listing for you, and include the values in Table 1. The cool thing is that you can use any of the hexadecimal values from Table 1 in the script, and it will tell you the size of the folder. You do not even have to use a coded value; you can just include a path to a folder, which I will show you later. Anyway, here is the code that assigns the value 0x5 to the $DocEnum variable (you could have figured this one out yourself):
$DocEnum = 0x5
Table 1 Shell Special Folder Values
Special folder name | Hexadecimal value |
ALTSTARTUP |
0x1d |
APPDATA |
0x1a |
BITBUCKET |
0xa |
COMMONALTSTARTUP |
0x1e |
COMMONAPPDATA |
0x23 |
COMMONDESKTOPDIR |
0x19 |
COMMONFAVORITES |
0x1f |
COMMONPROGRAMS |
0x17 |
COMMONSTARTMENU |
0x16 |
COMMONSTARTUP |
0x18 |
CONTROLS |
0x3 |
COOKIES |
0x21 |
DESKTOP |
0x0 |
DESKTOPDIRECTORY |
0x10 |
DRIVES |
0x11 |
FAVORITES |
0x6 |
FONTS |
0x14 |
HISTORY |
0x22 |
INTERNETCACHE |
0x20 |
LOCALAPPDATA |
0x1c |
MYPICTURES |
0x27 |
NETHOOD |
0x13 |
NETWORK |
0x12 |
PERSONAL |
0x5 |
PRINTERS |
0x4 |
PRINTHOOD |
0x1b |
PROFILE |
0x28 |
PROGRAMFILES |
0x26 |
PROGRAMFILESx86 |
0x30 |
PROGRAMS |
0x2 |
RECENT |
0x8 |
SENDTO |
0x9 |
STARTMENU |
0xb |
STARTUP |
0x7 |
SYSTEM |
0x25 |
SYSTEMx86 |
0x29 |
TEMPLATES |
0x15 |
WINDOWS |
0x24 |
We now need to create an instance of the shell.application object. Actually, we do not need to do this, but I thought I would do this, because it gives us access to all those special folders in Table 1. We could have used the WshShell object as seen here:
$wshshell = New-Object -ComObject wscript.shell $wshshell.SpecialFolders.Item(“mydocuments”)
But this method does not provide access to all the folders in Table 1. It only provides access to 18 folders. This is documented on MSDN.
The shell.application object is a COM object, and is created by using the New-Object cmdlet. We store the resulting object in the $shell variable. We then use the NameSpace method from the shell object to create a folder object that references the folder stored in the $DocEnum variable. When we have the folder object, we use the self property to refer to the specific folder, and store this in the $folderItem variable. We now query the path property and store it in the $path variable. This section of code is seen here. This code is very similar to the way we would have done things in VBScript:
$shell = New-Object -ComObject shell.application $myDocs = $shell.NameSpace($DocEnum) $folderitem = $myDocs.self $path = $folderitem.path
Now we are ready to use the Get-ChildItem cmdlet to get us a listing of all the files and folders in the target path. We use the -recurse parameter to allow the command to burrow down through any child folders. The -errorAction parameter tells the script to not report any errors from this command. This will suppress any “Access denied” errors you may get when targeting folders you might now have permission to:
$totalSize = Get-ChildItem -path $path -recurse -errorAction “SilentlyContinue”
We pipeline the results of the previous Get-ChildItem cmdlet to the Measure-Object cmdlet and tell it to sum the length of each file. This is shown here:
Measure-Object -property length -sum
Now we want to translate the results into something easier to read. The result from the previous command returned GenericMeasureInfo object which has a sum property. We want to translate that value into gigabytes or megabytes, if it makes sense. To do this we use a series of if statements. If the sum is bigger than 1,073,741,824, we will translate the value into gigabytes. To do this, we divide the $totalSize.Sum value by the 1GB admin constant. We use the -f format operator to display only two decimal places. This is seen here:
IF($totalSIze.Sum -ge 1073741824) { “{0:n2}” -f ($totalSize.sum / 1GB) + ” GigaBytes” }
If the size is bigger than 1,024, we will display the value in megabytes (MB). We use the ELSEIF statement to get to this section of thecode:
ELSEIF($totalSize.sum -ge 1024) { “{0:n2}” -f ($totalSize.sum / 1MB) + ” MegaBytes” }
However, if the size is less than 1,024, we display the value in kilobytes ((KB):
ELSE { “{0:n2}” -f ($totalSize.sum / 1KB) + ” KiloBytes” }
When we run the script, the folder size is returned:
So, LM, I hope this investigation into folder size magic does not cause me to lose my magician’s union membership (Local 42), but I just had to share this trick with you. David Blaine says hi.
Ed Wilson and Craig Liebendorfer, Scripting Guys
0 comments