Getting Large Files

Sooner or later, you're likely to have to "clean up" a disk that's running out of space. One of the simplest ways to do that is to find the really large files on the disk or in a directory and delete ones that you don't actually need, or move them to a location that has more space. Over the years, I've used multiple tools to find the large files, but these days I use PowerShell.

To start, we need to use Get-ChildItem with the -Recurse parameter, and use Sort-Object to sort by the Length property (and to avoid problems if there's any errors, we'll tell it to just ignore errors and keep right on going).

Get-ChildItem -Path $Home -Recurse -ErrorAction SilentlyContinue  `
       | Sort-Object -Property Length -Descending

That's good, but obviously we only need to only return the big files, not all of them, so let's grab only the 10 largest files:

Get-ChildItem -Path $Home -Recurse -ErrorAction SilentlyContinue  `
       | Sort-Object -Property Length -Descending `
       | Select-Object -First 10

OK, better, but kind of hard to read. So, let's do some formatting and cleanup...

Get-ChildItem -Path $Home -Recurse -ErrorAction SilentlyContinue  `
       | Sort-Object -Property Length -Descending  `
       | Select-Object -First 10 `
       | Select-Object Name, `
           @{Label='SizeMB';Expression={"{0:N0}" -f ($_.Length/1MB)}},`
           @{Label='LastWrite';Expression={"{0:d}" -f ($_.LastWriteTime)}}, `
           DirectoryName

Now we're getting closer. But my standard PowerShell window is only 120 characters wide, and that leaves the directory getting chopped off:

Name                                                                              SizeMB LastWrite  DirectoryName
----                                                                              ------ ---------  -------------
871790_001_spp-2016.10.0-SPP2016100.2016_1015.191.iso                             6,672  2016-11-23 C:\Users\Charlie...
en_windows_server_2016_x64_dvd_9327751.iso                                        5,392  2016-10-23 C:\Users\Charlie...
en_windows_server_2012_r2_with_update_x64_dvd_6052708.iso                         5,148  2015-01-09 C:\Users\Charlie...
14393.0.160715-1616.RS1_RELEASE_SERVER_EVAL_X64FRE_EN-US.ISO                      5,076  2016-09-27 C:\Users\Charlie...
en_windows_server_2016_essentials_x64_dvd_9327792.iso                             4,473  2016-10-23 C:\Users\Charlie...
en_windows_storage_server_2016_x64_dvd_9327790.iso                                4,363  2016-10-23 C:\Users\Charlie...
CDR-X10_1.10_for_Intel_X10_platform.iso                                           4,312  2015-12-29 C:\Users\Charlie...
en_windows_10_multiple_editions_version_1511_updated_apr_2016_x64_dvd_8705583.iso 4,252  2016-06-03 C:\Users\Charlie...
en_windows_10_multiple_editions_version_1607_updated_jul_2016_x64_dvd_9058187.iso 4,177  2016-09-21 C:\Users\Charlie...
en_windows_10_multiple_editions_x64_dvd_6846432.iso                               3,895  2015-07-30 C:\Users\Charlie...

Which isn't terribly helpful whenI start trying to actually identify the files PowerShell has found. So, let's take advantage of Format-Table's ability to wrap lines:

Get-ChildItem -Path $Home -Recurse -ErrorAction SilentlyContinue  `
     | Sort-Object -Property Length -Descending  `
     | Select-Object -First 10 `
     | Select-Object Name, `
         @{Label='SizeMB';Expression={"{0:N0}" -f ($_.Length/1MB)}},`
         @{Label='LastWrite';Expression={"{0:d}" -f ($_.LastWriteTime)}}, `
         DirectoryName `
     | Format-Table -auto -wrap

Now that works a bit better, but it ends up with an awful lot of column width for the filename, and a really narrow column for the DirectoryName on my machine. So, let's take it the last step and set some column widths. We can't do that with the Select-Object expressions we've been using, but we can do it with Format-Table expressions:

Get-ChildItem -Path $Home -Recurse -ErrorAction SilentlyContinue  `
     | Sort-Object -Property Length -Descending  `
     | Select-Object -First 10 `
     | Select-Object Name, `
         @{Label='SizeMB';Expression={"{0:N0}" -f ($_.Length/1MB)}},`
         @{Label='LastWrite';Expression={"{0:d}" -f ($_.LastWriteTime)}}, `
         DirectoryName `
     | Format-Table -Wrap `
                     @{Label='File Name';Expression={$_.Name};Width=50},`
                     @{Label='SizeMB';Expression={$_.SizeMB};Width=7},`
                     @{Label='Last Write';Expression={$_.LastWrite};Width=11},`
                     DirectoryName

Now that's a useful display. Notice that when we got to Format-Table, the object names we wanted for our columns now matched the calculated column names, not the original Get-ChildItem property names.

So, let's take the whole thing and wrap it up in a script, with comment-based help, of course. We'll use two parameters -- the number of files to return, and the starting path.

<#
.Synopsis
Find the 10 largest files in a directory tree

.Description
Get-myLargeFiles does a recursive search of a directory and its subdirectories
to find the largest files in that directory tree. By default, it searches from the 
top of the $home directory, and returns the 10 largest files, but you can specify
the starting directory and the number of files to return on the command line. 

.Example
Get-myLargeFiles 

Returns the 10 largest files in your personal directory tree ($home).

.Example
Get-myLargeFiles -Path 'C:\' -Number 20

Returns the 20 largest files on the C: drive. Note that this will not report any failures
caused by insufficient permissions to traverse a particular directory tree. 

.Parameter $Path
The path to the top of the search tree. Default value is $home

.Parameter $Number
The number of large files to return. Default value is 10.

.Inputs
[string]
[Int]

.Notes
    Author: Charlie Russel
 Copyright: 2016 by Charlie Russel
          : Permission to use is granted but attribution is appreciated
   Initial: 3/02/2016 (cpr)
   ModHist: 12/02/2016 (cpr) -- Set column widths in the Format-Table output. 
          :
#>
[CmdletBinding()]
Param(
     [Parameter(Mandatory=$False,Position=0)]
     [string]
     $Path = $Home,
     [Parameter(Mandatory=$False,Position=1)]
     [int]
     $Number = 10
     )

Get-ChildItem $path -recurse -ea SilentlyContinue  `
       | Sort-Object Length -Descending  `
       | Select-Object -first $Number  `
       | Select-Object Name, `
           @{Label='SizeMB';Expression={"{0:N0}" -f ($_.Length/1MB)}},`
           @{Label='LastWrite';Expression={"{0:d}" -f ($_.LastWriteTime)}}, `
           DirectoryName `
       | Format-Table -Wrap `
             @{Label='File Name';Expression={$_.Name};Width=50},`
             @{Label='SizeMB';Expression={$_.SizeMB};Width=7},`
             @{Label='Last Write';Expression={$_.LastWrite};Width=11},`
             @{Label='Directory Name';Expression={$_.DirectoryName}}

Leave a Reply

Your email address will not be published. Required fields are marked *