How to Get the Top 10 Largest Files on a Drive in Batch Script
When a hard drive suddenly runs out of space, the culprit is usually a handful of massive files, such as oversized log files, large downloads, forgotten virtual machine disks, or database backups. Manually hunting for these files through folders is slow and unreliable. A Batch script can automate this search by scanning folder structures and sorting all files by size. By retrieving the top 10 largest files, you can immediately identify where disk space is being consumed and take action to reclaim it.
This guide will explain how to find the largest files using PowerShell from a Batch script.
Method 1: Top 10 Largest Files on a Drive
PowerShell's Get-ChildItem with -Recurse scans an entire directory tree, and Sort-Object Length -Descending with Select-Object -First 10 produces a properly sorted top-N list, something that pure Batch cannot do (the sort command sorts lexicographically, not numerically).
Implementation
@echo off
setlocal
set "SearchPath=%~1"
set "TopN=10"
if "%SearchPath%"=="" (
echo Usage: %~nx0 ^<path^> [count]
echo.
echo Examples:
echo %~nx0 C:\
echo %~nx0 D:\Data
echo %~nx0 C:\Users 20
endlocal
exit /b 1
)
if not "%~2"=="" set "TopN=%~2"
if not exist "%SearchPath%\" (
echo [ERROR] Path not found: %SearchPath% >&2
endlocal
exit /b 1
)
echo [INFO] Searching "%SearchPath%" for the %TopN% largest files...
echo [INFO] This may take several minutes on large drives.
echo --------------------------------------------------
powershell -NoProfile -Command ^
"$results = Get-ChildItem -Path '%SearchPath%' -Recurse -File -Force -ErrorAction SilentlyContinue |" ^
" Sort-Object Length -Descending |" ^
" Select-Object -First %TopN% |" ^
" ForEach-Object {" ^
" $sizeStr = if ($_.Length -ge 1GB) { '{0:N1} GB' -f ($_.Length / 1GB) }" ^
" elseif ($_.Length -ge 1MB) { '{0:N1} MB' -f ($_.Length / 1MB) }" ^
" else { '{0:N1} KB' -f ($_.Length / 1KB) };" ^
" [PSCustomObject]@{" ^
" 'Size' = $sizeStr;" ^
" 'Bytes' = $_.Length;" ^
" 'Path' = $_.FullName" ^
" }" ^
" };" ^
"if ($results) {" ^
" $results | Format-Table Size, Path -AutoSize -Wrap;" ^
" $totalGB = [math]::Round(($results | Measure-Object Bytes -Sum).Sum / 1GB, 2);" ^
" Write-Host \"Total: $totalGB GB in $($results.Count) files\"" ^
"} else {" ^
" Write-Host 'No files found (access may be restricted).'" ^
"}"
echo --------------------------------------------------
endlocal
exit /b 0
Sample output:
Size Path
---- ----
4.2 GB C:\VMs\server2022.vhdx
2.8 GB C:\Users\admin\Downloads\ubuntu-22.04.iso
1.5 GB C:\ProgramData\Package Cache\vs_installer.exe
982.3 MB C:\Windows\MEMORY.DMP
645.0 MB C:\Users\admin\AppData\Local\Temp\extract_123.tmp
512.4 MB C:\Program Files\Docker\resources\docker.exe
389.1 MB C:\Windows\Installer\abc123.msp
287.6 MB C:\Logs\application_20240510.log
245.3 MB C:\ProgramData\Microsoft\Search\Data\Applications\Windows\Windows.edb
198.7 MB C:\Users\admin\Documents\database_backup.bak
Total: 11.72 GB in 10 files
Key PowerShell flags:
| Flag | Purpose |
|---|---|
-Recurse | Search all subdirectories |
-File | Return only files, not directories |
-Force | Include hidden and system files |
-ErrorAction SilentlyContinue | Skip inaccessible files/folders without displaying errors |
Why human-readable sizes:
Raw byte counts like 4509715456 are difficult to interpret at a glance. The script automatically formats sizes as GB, MB, or KB based on magnitude, while retaining the raw byte count for accurate sorting.
Method 2: Files Exceeding a Size Threshold
Sometimes you don't need a sorted top-N, you want to find every file above a certain size. This is useful for identifying files to clean up without needing to compare them.
@echo off
setlocal
set "SearchPath=%~1"
set "ThresholdMB=100"
set "LogFile=%~dp0large_files.csv"
if "%SearchPath%"=="" (
echo Usage: %~nx0 ^<path^> [threshold_MB]
echo.
echo Example: %~nx0 C:\Users 500
endlocal
exit /b 1
)
if not "%~2"=="" set "ThresholdMB=%~2"
if not exist "%SearchPath%\" (
echo [ERROR] Path not found: %SearchPath% >&2
endlocal
exit /b 1
)
echo [INFO] Finding files larger than %ThresholdMB% MB in "%SearchPath%"...
powershell -NoProfile -Command ^
"$threshold = %ThresholdMB% * 1MB; " ^
"$files = Get-ChildItem -Path '%SearchPath%' -Recurse -File -Force -ErrorAction SilentlyContinue | " ^
"Where-Object { $_.Length -ge $threshold } | Sort-Object Length -Descending; " ^
"if ($files.Count -gt 0) { " ^
" $files | ForEach-Object { $mb = [math]::Round($_.Length / 1MB,1); Write-Host ('{0,10} MB {1}' -f $mb, $_.FullName) }; " ^
" Write-Host ''; Write-Host ('Found {0} file(s) over %ThresholdMB% MB' -f $files.Count); " ^
" $totalGB = [math]::Round(($files | Measure-Object Length -Sum).Sum / 1GB,2); Write-Host ('Total: {0} GB' -f $totalGB); " ^
" $files | Select-Object FullName,@{N='SizeMB';E={[math]::Round($_.Length/1MB,1)}},LastWriteTime | Export-Csv -Path '%LogFile%' -NoTypeInformation; " ^
" Write-Host ('CSV saved: %LogFile%'); " ^
"} else { Write-Host 'No files found above the threshold.' }"
endlocal
exit /b 0
Why this is often more useful than top-N:
On a nearly full drive, the problem may not be one giant file, it could be dozens of 200–500 MB log files scattered across directories. A threshold search finds all of them, while a top-10 would miss files ranked 11th and below.
The CSV export includes LastWriteTime:
Files that haven't been modified in months or years are strong candidates for cleanup. The timestamp helps you distinguish between active data files and forgotten leftovers.
Method 3: Targeted Search by File Type
When you know what type of file is consuming space (log files, VM disks, database backups), a targeted search is much faster than scanning everything.
@echo off
setlocal
set "SearchPath=%~1"
set "Extension=%~2"
if "%Extension%"=="" (
echo Usage: %~nx0 ^<path^> ^<extension^>
echo.
echo Examples:
echo %~nx0 C:\ .log
echo %~nx0 D:\Data .bak
echo %~nx0 C:\Users .iso
endlocal
exit /b 1
)
echo [INFO] Finding largest *%Extension% files in "%SearchPath%"...
powershell -NoProfile -Command ^
"$files = Get-ChildItem -Path '%SearchPath%' -Recurse -File -Force" ^
" -Filter '*%Extension%' -ErrorAction SilentlyContinue |" ^
" Sort-Object Length -Descending |" ^
" Select-Object -First 10;" ^
"if ($files) {" ^
" $files | ForEach-Object {" ^
" $sz = if ($_.Length -ge 1GB) { '{0:N1} GB' -f ($_.Length/1GB) }" ^
" else { '{0:N1} MB' -f ($_.Length/1MB) };" ^
" Write-Host ('{0,10} {1} {2}' -f $sz, $_.LastWriteTime.ToString('yyyy-MM-dd'), $_.FullName)" ^
" };" ^
" $totalGB = [math]::Round(($files | Measure-Object Length -Sum).Sum / 1GB, 2);" ^
" Write-Host \"\";" ^
" Write-Host \"Total: $totalGB GB in $($files.Count) files\"" ^
"} else {" ^
" Write-Host 'No *%Extension% files found.'" ^
"}"
endlocal
exit /b 0
Common file types to search for:
| Extension | Typical Source | Often Safe to Clean? |
|---|---|---|
.log | Application/server logs | Yes (after archiving) |
.bak | Database backups | Yes (keep recent only) |
.iso | Disk images | Yes (if no longer needed) |
.vhdx / .vmdk | Virtual machine disks | Depends on VM status |
.dmp | Crash dumps | Yes (after analysis) |
.tmp | Temporary files | Usually yes |
.cab / .msp | Windows Update/installer cache | Caution, may be needed for uninstalls |
Why -Filter instead of -Include:
Get-ChildItem -Filter passes the filter to the filesystem API, making it significantly faster than -Include, which retrieves all files first and filters in PowerShell. On large drives, this difference can be minutes vs. seconds.
How to Avoid Common Errors
Problem: Access Denied Errors Fill the Screen
When scanning system drives, the script will encounter protected directories (System Volume Information, $Recycle.Bin, Windows service folders) that deny access to even administrators.
Solution: All methods in this guide use -ErrorAction SilentlyContinue to suppress access-denied errors. The script continues scanning accessible locations silently.
Problem: Symbolic Links and Junctions Cause Infinite Loops
Directories like C:\Users\All Users are NTFS junctions that point to other directories. Following them recursively can create infinite loops or duplicate results.
Solution: PowerShell's Get-ChildItem -Recurse handles most junctions correctly by default and does not follow them. If you encounter issues, add -Attributes !ReparsePoint to explicitly exclude reparse points (junctions and symlinks).
Problem: Scanning Takes Too Long on Large Drives
A full recursive scan of a system drive with millions of files can take 10–30 minutes.
Solution: Narrow the search scope:
- Target specific directories (
C:\Users,D:\Data) rather than the root. - Use Method 3 to search for specific file types.
- Use Method 2 with a high threshold (e.g., 500 MB) to skip small files quickly.
Wrong Way: Using Batch for /r with set /a for Size Comparison
:: BROKEN: set /a overflows at ~2 GB
for /r "C:\" %%a in (*) do (
set /a "size=%%~za"
if !size! gtr 104857600 echo %%~fa
)
Batch's set /a is limited to 32-bit integers (~2.1 GB). Any file larger than 2 GB produces an overflow, and the comparison fails silently. The if %%~za GTR comparison also fails for very large numbers because if uses signed 32-bit comparison.
Correct Way: Use PowerShell for all file size comparisons and sorting. PowerShell's [long] type handles files up to 8 exabytes.
Best Practices and Rules
1. Search Data Directories First
Scanning C:\Windows reveals files you usually cannot delete. Start with user-accessible locations (C:\Users, D:\Data, download folders) for the most actionable results.
2. Run as Administrator for Complete Results
Without elevation, the scan silently skips protected directories. Files in C:\ProgramData, C:\Windows\Temp, and service-owned folders will be missed. Run as administrator for a complete inventory.
3. Include Last Modified Date
A 2 GB file modified yesterday is probably in active use. A 2 GB file not touched in 18 months is a cleanup candidate. Always include the modification date in your output to make informed decisions.
4. Export Results for Review
Don't make cleanup decisions based on a scrolling console display. Export to CSV (Method 2) so you can review, sort, and filter the results before deleting anything. Send the CSV to the system owner for approval before cleanup.
5. Never Delete Automatically
A "find and delete largest files" script is extremely dangerous. Always separate the discovery step (this guide) from the cleanup step. Review the results, confirm with stakeholders, and delete manually or with a separate, reviewed script.
Conclusions
Identifying the largest files on a drive is the fastest way to resolve a disk space crisis. By using PowerShell's recursive search with proper numeric sorting, human-readable size formatting, and flexible filtering (by count, threshold, or file type), you gain deep visibility into storage consumption that would take hours to replicate manually. This capability is essential for proactive storage management, server auditing, and emergency space recovery.