Ad Space here

Welcome Guest! To enable all features please Login or Register.

Notification

Icon
Error

Share
Options
Go to last post Go to first unread
Dustin Higgins  
#1 Posted : Thursday, January 30, 2020 11:39:38 PM(UTC)
Dustin Higgins

Rank: Advanced Member

Groups: Registered
Joined: 7/1/2018(UTC)
Posts: 60
_United States

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

Sponsor
Ad Space here
Rss Feed  Atom Feed
Users browsing this topic
Forum Jump  
You cannot post new topics in this forum.
You cannot reply to topics in this forum.
You cannot delete your posts in this forum.
You cannot edit your posts in this forum.
You cannot create polls in this forum.
You cannot vote in polls in this forum.