

Rank: Advanced Member





Joined: 7/1/2018(UTC)

Posts: 63



Groups: RegisteredJoined: 7/1/2018(UTC)Posts: 63

Thanks: 1 times

Was thanked: 6 time(s) in 6 post(s)



"Sizer" Disk space has always been a problem for the 20 years that I have spent in the IT field, and it doesn't seem to be going away anytime soon. The crux of the problem is not necessarily the size of the disks; the operating systems, applications, and user projects just keep getting bigger. It really only takes one application, one user, or one system log file to run a disk right out of space. Servers and workstations share the same issue. Having enough disk space to operate is one of the most important proactive items that an administrator can monitor. Please see my article on LinkedIn about disk space. Even if you have a list of machines that are low on disk space, as trivial as it might sound, how does one efficiently locate the big files / directories? Introducing the "Sizer". Discussion about Sizer and Quick Demo on YouTube ​​​​​​​ The Sizer utilizes Robocopy.exe to quickly list the size of folders. With Sizer, you can start at the root of the C:\ drive and quickly drill down to locate problem areas. There are many posts that illustrate how to list folder sizes with Robocopy.exe. I give them all credit. Robocopy.exe is freaking awesome! Using that method, along with capturing and sizing subfolders is really the key to quickly finding where the disk space is being taken. I also used PowerShell, a Process with a Timeout only to start the Robocopy.exe process and capture the StdOut/StdErr streams. Post Sizer, there is no more manually checking common problem areas. Sizer will quickly tell you exactly where the problem areas are, and that equals efficiency. Here is the code (Don't forget Start-ProcessWaitTimeout mentioned above): All of the code goes in a single ps1 file IE: "Sizer.ps1". The parameters must be first. Parameters for the script: Code: param ( [Parameter(Mandatory=$true)] [string]$Dir = $NULL, [switch]$MB = $FALSE, [switch]$GB = $FALSE, [switch]$Auto = $FALSE, [switch]$CSV = $FALSE, [switch]$OBJECT = $FALSE ) Function to get a directory list using the Get-ChildItem command: Code: function Get-DirectoryList { <# .SYNOPSIS Funtion to get a list of directory names using the Get-ChildItem command .DESCRIPTION Funtion to get a list of directory names using the Get-ChildItem command #> param ( [Parameter(Mandatory=$true)] [string]$Directory = $null ) # Create the Object $Obj = [PSCustomObject]@{ Directory = $Directory DirList = New-Object System.Collections.Generic.List[string] } # Add a trailing backslash if it is not there # This will handle the case of C: and default it to the root if ($Directory.EndsWith("\") -eq $false) { $Directory = "$($Directory)\" } $Directories = Get-ChildItem -Force -Directory "$($Directory)*" # Add the Directories to the list foreach ($i in $Directories) { # Don't want links if ($i.Attributes -notmatch "ReparsePoint") { $Obj.DirList.Add($i.Name) } } return $Obj } Function that uses Robocopy to get folder sizes: Code: function Get-RobocopyDirectory { <# .SYNOPSIS Function that utilizes Robocopy to get the size of directories in the directory that is passed to it .DESCRIPTION Function that utilizes Robocopy to get the size of directories in the directory that is passed to it #> param ( [Parameter(Mandatory=$true)] [string]$Directory = $null, [switch]$IgnoreSubDirs = $false ) # Create the Object $Obj = [PSCustomObject]@{ Directory = $Directory DirList = New-Object System.Collections.Generic.List[string] Cmd = $null Size = $null RawSize = $null } $Directory = $Directory.TrimEnd("\") if ($Directory.EndsWith(":") -eq $true) { $Directory = "$($Directory)\\" } # Setup the command $Cmd = "C:\Windows\System32\robocopy.exe" # Setup the arguments if ($IgnoreSubDirs -eq $false) { $CmdArgs = @("""$($Directory)""","""NULL""","/l","/e","/njh","/nfl","/xj","/r:0","/w:0","/bytes") } elseif ($IgnoreSubDirs -eq $true) { $CmdArgs = @("""$($Directory)""","""NULL""","/l","/njh","/nfl","/xj","/r:0","/w:0","/bytes") } $Obj.Cmd = Start-ProcessWaitTimeout -Computer $null -CmdLine $cmd -CmdLineArgs $CmdArgs -Timeout 180 # Add a trailing backslash to the directory (so it matches Robocopy output) if ($Directory.EndsWith("\") -eq $false) { $Directory = "$($Directory)\" } # Loop through the StdOut foreach ($i in $Obj.Cmd.ProcessStdOut.Split("`n")) { # If it is a directory, get the name from the Robocopy output and add it to the list if it isn't there if ($i -match ".*\sDir\s+\d+\s+(?<Directory>.*)\s+") { if ($Matches.Directory -ne $Directory) { $RelativeSubDir = $Matches.Directory.Replace($Directory,"") $SubDir = $RelativeSubDir.SubString(0,$RelativeSubDir.IndexOf("\")) if ($Obj.DirList.Contains($SubDir) -eq $false) { $Obj.DirList.Add($SubDir) } } } # Get the size and round to 2 places if ($i -match "Bytes\s:\s+(?<Size>\d+)\s.*") { $Obj.RawSize = $Matches.Size if ($MB -or ($Auto -and ([math]::Round(($Matches.Size / 1MB),2) -lt 1024))) { $Obj.Size = [math]::Round(($Matches.Size / 1MB),2).ToString("0.00").PadRight(9," ") + "MB" } elseif ($GB -or ($Auto -and ([math]::Round(($Matches.Size / 1MB),2) -ge 1024))) { $Obj.Size = [math]::Round(($Matches.Size / 1GB),2).ToString("0.00").PadRight(9," ") + "GB" } } } # Return the Object Return $Obj } Main: Code: ################################################# # # Main # ################################################# # Input validation: Only one of the 3 input flags (MB, GB, Auto) can be set at a time if (([bool]$MB + [bool]$GB + [bool]$Auto) -gt 1) { Write-Host "Invalid Options. -MB, -GB, and -Auto cannot be combined with each other" -ForegroundColor Red EXIT 2 } elseif (([bool]$MB + [bool]$GB + [bool]$Auto) -eq 0) { $Auto = $True } # Just for the root, use Get-ChildItem to get a list of the directories. It is a lot faster than using Robocopy. # Matching on IE: (C: or C:\) or IE: (\\computer\c$ or \\computer\c$\) if ($Dir -match "[A-Z]:\\*\Z" -or $Dir -match "\A\\\\.*\\[A-Z]\$\\*\Z") { # Get a list of directories at the root $DirObj = Get-DirectoryList -Directory $Dir } else { # Not the root, just size the directory (that will get a list of directories also) $DirObj = Get-RobocopyDirectory -Directory "$($Dir)" } # Get longest directory name for display purposes $LongestDir = 0 foreach ($i IN $DirObj.DirList) { IF (("$($Dir.TrimEnd("\"))\$($i)").length -gt $LongestDir) {$LongestDir = ("$($Dir.TrimEnd("\"))\$($i)").length} } if ($Dir.length -gt $LongestDir) {$LongestDir = $Dir.Length} $Width = $LongestDir + 5 $Lines = "".PadRight(($Width + 14), "-") $ToBecomeObject = New-Object System.Collections.Generic.List[object] # Write visual header if normal mode, and csv header if CSV mode if ((!($CSV)) -and (!($OBJECT))) { Write-Host $Lines Write-Host " SubFolder Size(s):" Write-Host $Lines } elseif ($OBJECT) { $ToBecomeObject.Add("Directory,Size_MB,Size_GB") } else { "Directory,Size_MB,Size_GB" } # Size the directories $TotalRawSize = 0 foreach ($i in $DirObj.DirList) { $RoboObj = Get-RobocopyDirectory -Directory "$($Dir.TrimEnd("\"))\$($i)" $TotalRawSize += $RoboObj.RawSize if ((!($CSV)) -and (!($OBJECT))) { Write-Host " $($RoboObj.Directory.PadRight($Width," ")) $($RoboObj.Size)" -ForegroundColor Cyan } elseif ($OBJECT) { $ToBecomeObject.Add("$($RoboObj.Directory),$([math]::Round(($RoboObj.RawSize / 1MB),2).ToString("0.00")),$([math]::Round(($RoboObj.RawSize / 1GB),2).ToString("0.00"))") } else { "$($RoboObj.Directory),$([math]::Round(($RoboObj.RawSize / 1MB),2).ToString("0.00")),$([math]::Round(($RoboObj.RawSize / 1GB),2).ToString("0.00"))" } } if ((!($CSV)) -and (!($OBJECT))) { Write-Host $Lines Write-Host " Total Size:" Write-Host $Lines } # Get the final size of the whole $Dir $WholeDirObj = Get-RobocopyDirectory -Directory "$($Dir)" -IgnoreSubDirs $TotalRawSize += $WholeDirObj.RawSize # Format TotalSize based on input switches if ($MB -or ($Auto -and ([math]::Round(($TotalRawSize / 1MB),2) -lt 1024))) { $TotalSize = [math]::Round(($TotalRawSize / 1MB),2).ToString("0.00").PadRight(9," ") + "MB" } elseif ($GB -or ($Auto -and ([math]::Round(($TotalRawSize / 1MB),2) -ge 1024))) { $TotalSize = [math]::Round(($TotalRawSize / 1GB),2).ToString("0.00").PadRight(9," ") + "GB" } if ((!($CSV)) -and (!($OBJECT))) { Write-Host " $($WholeDirObj.Directory.PadRight($Width," ")) $($TotalSize)" -ForegroundColor Cyan Write-Host $Lines Write-Host "" } elseif ($OBJECT) { $ToBecomeObject.Add("$($WholeDirObj.Directory),$([math]::Round(($TotalRawSize / 1MB),2).ToString("0.00")),$([math]::Round(($TotalRawSize / 1GB),2).ToString("0.00"))") $SizeObj = ConvertFrom-Csv ($ToBecomeObject) $SizeObj | % { $_.Size_MB = [double]$_.Size_MB } $SizeObj | % { $_.Size_GB = [double]$_.Size_GB } $SizeObj } else { "$($WholeDirObj.Directory),$([math]::Round(($TotalRawSize / 1MB),2).ToString("0.00")),$([math]::Round(($TotalRawSize / 1GB),2).ToString("0.00"))" } Follow Dustin Higgins ​​​​​​​on Twitter and Instagram Edited by moderator Monday, March 02, 2020 1:30:06 AM(UTC) | Reason: Not specified