Hey, Scripting Guy! Can I Determine Which Folders Are Using the Most Space on My Computer?
Hey Scripting Guy! I have a laptop that I bought last year with Windows Vista installed on it. I love it. However, I have been reading all the positive reviews surrounding Windows 7, and I think I am going to install Windows 7 as soon as it is available. In fact, I have already pre-ordered it. The thing is that I do not like to do upgrades—I have never trusted them because I upgraded my computer at work from Windows 3.11 to Windows 95 when it came out, and I had nothing but problems until I did the wipe and reload process. In preparation for upgrading my laptop to Windows 7, I am trying to find all of my data. The laptop has a respectable 150 GB hard drive, but it is nearly full. I need help identifying which folders are eating up all of my disk space. I have a 200 GB portable drive I will use to back up my data from the laptop, but I do not want to have a nice “new” laptop that is sucking fumes in the disk space department. Can you help me?
— EC
Hello EC,
The ink blue sky hung down heavy upon the whiskey bottle strewn alley leading to the 100-year-old brownstone. A shadow slowly slithered up the wall and a sense of foreboding crept up my spine like a 70-year-old man navigating the narrow stairs to the top of the Statue of Liberty. A clinking sound cut through the smog-laden air like a dull butter knife attempting to part a bagel. It seems like only last year when my new Windows Vista laptop arrived via the early evening overnight delivery service. Disk space has been gobbled up like Inky, Blinky, Pinky, and Clyde consuming dots. (Microsoft Scripting Guy Ed Wilson here. If you have friended me on Facebook, you know that after I finished writing the recent article for TechNet Magazine, I also finished reading a Mickey Spillane novel. I was just trying to get into the mood to solve the mystery of the missing disk space.) The cool thing is we have the tools to solve the mystery.
This week we will be reviewing some of the scripts that were submitted during the recently held 2009 Summer Scripting Games. The description of the 2009 Summer Scripting Games details all of the events. Each of the events was answered by a globally recognized expert in the field. There were some cool prizes and winners were recognized from around the world. Additionally, just like at the “real Olympics” because there was a lot going on, an “if you get lost page” was created. Communication with participants was maintained via Twitter, Facebook, and a special forum. (The special forum has been taken down, but Twitter and Facebook are still used to communicate with Hey, Scripting Guy! fans). We will be focusing on solutions that used Windows PowerShell. We have several good introduction to Windows PowerShell Hey, Scripting Guy! articles that you will find helpful.
The Beginner Event 8 was the pole vault in the 2009 Summer Scripting Games, and the task was to write a script that identifies which folders are consuming the most disk space. Tojo2000 submitted a function for the solution to the Beginner Event 8. The Windows PowerShell 2.0 function is seen here.
(Note: The original Tojo2000 function from PoshCode has a problem with the help tags when it is run on the RTM version of Windows PowerShell 2.0. This is because the returnvalue tag changed the name to outputs after the community technology preview 3 version. I have corrected it here.)
Get-DirSizeFunction.ps1
function Get-DirSize {
<#
.Synopsis
Gets a list of directories and sizes.
.Description
This function recursively walks the directory tree and returns the size of
each directory found.
.Parameter path
The path of the root folder to start scanning.
.Example
(Get-DirSize $env:userprofile | sort Size)[-2]
Get the largest folder under the user profile.
.Example
Get-DirSize -path “c:data” | Sort-Object -Property size -Descending
Displays folders and sub folders from c:data in descending size order
.Outputs
[PSObject]
.Notes
NAME: Get-DirSize
AUTHOR: ToJo2000
LASTEDIT: 8/12/2009
KEYWORDS: 2009 Summer Scripting Games, Beginner Event 8, Get-ChildItem, Files and Folders
.Link
Http://www.ScriptingGuys.com
#Requires -Version 2.0
#>
param([Parameter(Mandatory = $true, ValueFromPipeline = $true)][string]$path)
BEGIN {}
PROCESS{
$size = 0
$folders = @()
foreach ($file in (Get-ChildItem $path -Force -ea SilentlyContinue)) {
if ($file.PSIsContainer) {
$subfolders = @(Get-DirSize $file.FullName)
$size += $subfolders[-1].Size
$folders += $subfolders
} else {
$size += $file.Length
}
}
$object = New-Object -TypeName PSObject
$object | Add-Member -MemberType NoteProperty -Name Folder `
-Value (Get-Item $path).FullName
$object | Add-Member -MemberType NoteProperty -Name Size -Value $size
$folders += $object
Write-Output $folders
}
END {}
} # end function Get-DirSize
The nice thing about functions is that you have a lot of flexibility in how you use them. The problem with a function is you have to load it somehow before you can use it. This is not a problem, but it is an extra step that most VBScripters are not used to having to perform. Perhaps the easiest thing to do with a function is to save it into a separate file, and then include it into your Windows PowerShell console session. To include the function into your Windows PowerShell session, you precede the path to the file with a period. You can then work with the function in the same manner you would with a cmdlet. (In fact, the early name for Windows PowerShell 2.0 advanced functions was “script cmdlets.”) Once the function is loaded into the Windows PowerShell console, you can use the Get-Help cmdlet to get help from the function. You can use the function directly or even pipe the output from the function into another cmdlet such as the Sort-Object cmdlet. One thing that is great about Windows PowerShell 2.0 and functions is that tab expansion even works when using them interactively. This is seen here:
In addition to working with functions directly from within the Windows PowerShell console, you have the option to include the function in your Windows PowerShell profile, in a function library, or a module (modules are a Windows PowerShell 2.0 feature). Of course, you can also put functions into a script, and that is what I decided to do.
The ScriptingGamesBeginnerEvent8.ps1 Windows PowerShell script uses the Tojo2000 Get-DirSize function and adds command-line parameters, sorting, and formatting features. Because Tojo2000 had already incorporated Windows PowerShell 2.0 help tags into his script (which will not work on Windows PowerShell 1.0), I decided to go ahead and add additional help to the script and to my number-formatting function. It is very likely that the bulk of your computers are still running Windows PowerShell 1.0. It is a best practice when using a feature from Windows PowerShell 2.0 to include the requires version 2.0 tag. If you are not sure what those features are, I would recommend that for any script you write using Windows PowerShell 2.0 you either test extensively on Windows PowerShell 1.0 or you include the requires version 2.0 tag in all of your scripts. The syntax for this command is seen here:
#requires –version 2.0
The complete ScriptingGamesBeginnerEvent8.ps1 script is seen here.
ScriptingGamesBeginnerEvent8.ps1
<#
.Synopsis
Lists the five largest folders and their size
.Description
This script lists the five largest folders and their size
.Example
ScriptingGamesBeginnerEvent8.ps1 -path C:fso -first 3
Returns the three largest folders from within the C:fso parent directory
.Inputs
[string]
.OutPuts
[string]
.Notes
NAME: ScriptingGamesBeginnerEvent8.ps1
AUTHOR: Ed Wilson
LASTEDIT: 5/20/2009
KEYWORDS: 2009 SUmmer Scripting Games, Beginner Event 8, Get-ChildItem, Files and Folders
.Link
Http://www.ScriptingGuys.com
#Requires -Version 2.0
#>
Param(
[string]$path = “c:data1”,
[int]$first = 5
)# end param
# *** Begin Functions ***
function Get-DirSize {
<#
.Synopsis
Gets a list of directories and sizes.
.Description
This function recursively walks the directory tree and returns the size of
each directory found.
.Parameter path
The path of the root folder to start scanning.
.Example
(Get-DirSize $env:userprofile | sort Size)[-2]
Get the largest folder under the user profile.
.Example
Get-DirSize -path “c:data” | Sort-Object -Property size -Descending
Displays folders and sub folders from c:data in descending size order
.Outputs
[PSObject]
.Notes
NAME: Get-DirSize
AUTHOR: ToJo2000
LASTEDIT: 8/12/2009
KEYWORDS: 2009 Summer Scripting Games, Beginner Event 8, Get-ChildItem, Files and Folders
.Link
Http://www.ScriptingGuys.com
#Requires -Version 2.0
#>
param([Parameter(Mandatory = $true, ValueFromPipeline = $true)][string]$path)
BEGIN {}
PROCESS{
$size = 0
$folders = @()
foreach ($file in (Get-ChildItem $path -Force -ea SilentlyContinue)) {
if ($file.PSIsContainer) {
$subfolders = @(Get-DirSize $file.FullName)
$size += $subfolders[-1].Size
$folders += $subfolders
} else {
$size += $file.Length
}
}
$object = New-Object -TypeName PSObject
$object | Add-Member -MemberType NoteProperty -Name Folder `
-Value (Get-Item $path).FullName
$object | Add-Member -MemberType NoteProperty -Name Size -Value $size
$folders += $object
Write-Output $folders
}
END {}
} # end function Get-DirSize
Function Get-FormattedNumber($size)
{
<#
.Synopsis
Formats a number into Gig, Meg, or Kilo
.Description
This function will format a number that is passed in bytes into
Gigabytes, Megabytes, or Kilobytes as necessary. It displays two
decimal places and the appropriate string qualifier. It should
be used only to format string output. This function does not maintain
technical precision, but rounds to the nearest two decimal places.
.Example
Get-FormattedNumber -size 1025
Displays 1.00 KiloBytes
.Example
Get-FormattedNumber -size 1026
Displays 9.79 MegaBytes
.Example
Get-FormattedNumber -size 10261024
Displays 1.00 KiloBytes
.Inputs
[int32]
.OutPuts
[string]
.Notes
NAME: Get-FormattedNumber
AUTHOR: Ed Wilson
LASTEDIT: 8/12/2009
KEYWORDS: Format number, admin constants, if/elseif/else,
Dot Net framework format specifier, .NET Framework
.Link
Http://www.ScriptingGuys.com
#Requires -Version 2.0
#>
IF($size -ge 1GB)
{
“{0:n2}” -f ($size / 1GB) + ” GigaBytes”
}
ELSEIF($size -ge 1MB)
{
“{0:n2}” -f ($size / 1MB) + ” MegaBytes”
}
ELSE
{
“{0:n2}” -f ($size / 1KB) + ” KiloBytes”
}
} #end function Get-FormattedNumber
# *** Entry Point to Script ***
if(-not(Test-Path -Path $path))
{
Write-Host -ForegroundColor red “Unable to locate $path”
Help $MyInvocation.InvocationName -full
exit
}
Get-DirSize -path $path |
Sort-Object -Property size -Descending |
Select-Object -Property folder, size -First $first |
Format-Table -Property Folder,
@{ Label=”Size of Folder” ; Expression = {Get-FormattedNumber($_.size)} }
The entry point to the ScriptingGamesBeginnerEvent8.ps1 script uses the Test-Path cmdlet to determine if the path that is supplied to the script exists. If it does not exist, a message is displayed on the screen that states the path cannot be located. The help for the script is then displayed on the console, and the script exits. This is seen here:
After the directory is determined to exist, the Get-DirSize function is called where the Get-ChildItem cmdlet is used to retrieve directory size information. After the directory sizes are retrieved, a custom PSObject is created and returned to the calling code. The PSObjects are piped to the Sort-Object cmdlet where the sizes are sorted and to the Select-Object cmdlet where the largest folders are selected. This is seen here:
Get-DirSize -path $path |
Sort-Object -Property size -Descending |
Select-Object -Property folder, size -First $first |
Because the size of the folders is returned in bytes, I decided to write a function that would convert the sizes to something a bit more readable. In the Get-FormattedNumber function, the size of the number is used to determine whether the number will be converted to kilobytes, megabytes, or gigabytes. The newly formatted number is returned to the Format-Table cmdlet as a custom property. This is seen here:
Format-Table -Property Folder,
@{ Label=”Size of Folder” ; Expression = {Get-FormattedNumber($_.size)} }
When the script is run, the following output is displayed:
PS C:Usersedwils> C:dataScriptingGuysHSG_8_17_09ScriptingGamesBeginnerEvent8.ps1
Folder Size of Folder
—— ————–
C:data &nbs
0 comments