Files
linux-scripts/windows-disk-usage-reporter.ps1
chiefgeek a1a17e81a1 Sync all scripts from website downloads — 352 scripts total
Includes updated JS challenge scripts with Claude-User whitelist,
same-site referer bypass, Blackbox-Exporter allowed bot, and all
new exporters, cheat sheets, and automation scripts.
2026-05-25 03:31:08 +02:00

414 lines
14 KiB
PowerShell

#Requires -Version 5.1
#########################################################################################
#### windows-disk-usage-reporter.ps1 - Find what's consuming disk space on Windows ####
#### Scans volumes, ranks largest directories and files, flags old data ####
#### ####
#### Author: Phil Connor ####
#### Contact: contact@mylinux.work ####
#### License: MIT ####
#### Version 1.00 ####
#### ####
#### Usage: ####
#### .\windows-disk-usage-reporter.ps1 ####
#### .\windows-disk-usage-reporter.ps1 -Path C:\Users ####
#### .\windows-disk-usage-reporter.ps1 -TopN 50 -MinSizeMB 100 ####
#### .\windows-disk-usage-reporter.ps1 -Json ####
#### ####
#########################################################################################
[CmdletBinding()]
param(
[string]$Path = "C:\",
[int]$TopN = 20,
[int]$MinSizeMB = 1,
[int]$MaxDepth = 3,
[int]$AgeWarnDays = 90,
[switch]$Json,
[switch]$NoColor
)
$Version = "1.00"
$MinSizeBytes = [int64]$MinSizeMB * 1048576
$AgeCutoff = (Get-Date).AddDays(-$AgeWarnDays)
$Separator = [string]::new([char]0x2500, 88)
# ============================================================================
# HELPERS
# ============================================================================
function Format-FileSize {
param([int64]$Bytes)
if ($Bytes -ge 1TB) { return "{0:N2} TB" -f ($Bytes / 1TB) }
if ($Bytes -ge 1GB) { return "{0:N2} GB" -f ($Bytes / 1GB) }
if ($Bytes -ge 1MB) { return "{0:N1} MB" -f ($Bytes / 1MB) }
if ($Bytes -ge 1KB) { return "{0:N1} KB" -f ($Bytes / 1KB) }
return "$Bytes B"
}
function Write-ColorLine {
param(
[string]$Text,
[ConsoleColor]$Color = [ConsoleColor]::Gray
)
if ($NoColor) {
Write-Output $Text
} else {
Write-Host $Text -ForegroundColor $Color
}
}
function Write-Header {
param([string]$Title)
Write-ColorLine ""
Write-ColorLine ("=" * 56) Cyan
Write-ColorLine " $Title" Cyan
Write-ColorLine ("=" * 56) Cyan
Write-ColorLine ""
}
function Get-DirectorySizeRecursive {
param(
[string]$DirPath,
[int]$CurrentDepth,
[int]$Limit
)
if ($CurrentDepth -gt $Limit) { return @() }
$results = [System.Collections.Generic.List[PSCustomObject]]::new()
try {
$items = Get-ChildItem -LiteralPath $DirPath -Directory -ErrorAction SilentlyContinue
} catch {
return @()
}
foreach ($dir in $items) {
try {
$files = Get-ChildItem -LiteralPath $dir.FullName -Recurse -File -ErrorAction SilentlyContinue
$totalSize = ($files | Measure-Object -Property Length -Sum -ErrorAction SilentlyContinue).Sum
if ($null -eq $totalSize) { $totalSize = 0 }
$results.Add([PSCustomObject]@{
Path = $dir.FullName
SizeBytes = [int64]$totalSize
})
} catch {
continue
}
if ($CurrentDepth -lt $Limit) {
$children = Get-DirectorySizeRecursive -DirPath $dir.FullName -CurrentDepth ($CurrentDepth + 1) -Limit $Limit
foreach ($child in $children) {
$results.Add($child)
}
}
}
return $results
}
# ============================================================================
# VOLUME OVERVIEW
# ============================================================================
function Get-VolumeOverview {
Write-Header "Volume Overview"
$headerLine = " {0,-12} {1,-20} {2,10} {3,10} {4,6}" -f "Drive", "Label", "Size", "Free", "Used%"
Write-ColorLine $headerLine White
Write-ColorLine " $Separator"
$volumes = @()
try {
$disks = Get-CimInstance -ClassName Win32_LogicalDisk -Filter "DriveType=3" -ErrorAction SilentlyContinue
} catch {
$disks = Get-WmiObject -Class Win32_LogicalDisk -Filter "DriveType=3" -ErrorAction SilentlyContinue
}
foreach ($disk in $disks) {
$total = [int64]$disk.Size
$free = [int64]$disk.FreeSpace
if ($total -eq 0) { continue }
$used = $total - $free
$pct = [math]::Round(($used / $total) * 100, 1)
$color = [ConsoleColor]::Green
if ($pct -ge 90) { $color = [ConsoleColor]::Red }
elseif ($pct -ge 80) { $color = [ConsoleColor]::Yellow }
$line = " {0,-12} {1,-20} {2,10} {3,10} {4,5}%" -f $disk.DeviceID, $disk.VolumeName, (Format-FileSize $total), (Format-FileSize $free), $pct
Write-ColorLine $line $color
$volumes += [PSCustomObject]@{
Drive = $disk.DeviceID
Label = $disk.VolumeName
SizeBytes = $total
FreeBytes = $free
UsedPct = $pct
}
}
return $volumes
}
# ============================================================================
# TOP DIRECTORIES BY SIZE
# ============================================================================
function Get-TopDirectories {
Write-Header "Top $TopN Directories by Size"
$headerLine = " {0,4} {1,-60} {2,10}" -f "#", "Directory", "Size"
Write-ColorLine $headerLine White
Write-ColorLine " $Separator"
$allDirs = Get-DirectorySizeRecursive -DirPath $Path -CurrentDepth 1 -Limit $MaxDepth
$topDirs = $allDirs | Sort-Object SizeBytes -Descending | Select-Object -First $TopN
$rank = 0
foreach ($dir in $topDirs) {
$rank++
$color = [ConsoleColor]::Gray
if ($dir.SizeBytes -ge 10GB) { $color = [ConsoleColor]::Red }
elseif ($dir.SizeBytes -ge 1GB) { $color = [ConsoleColor]::Yellow }
$displayPath = $dir.Path
if ($displayPath.Length -gt 58) { $displayPath = "..." + $displayPath.Substring($displayPath.Length - 55) }
$line = " {0,4} {1,-60} {2,10}" -f $rank, $displayPath, (Format-FileSize $dir.SizeBytes)
Write-ColorLine $line $color
}
return $topDirs
}
# ============================================================================
# TOP FILES BY SIZE
# ============================================================================
function Get-TopFiles {
Write-Header "Top $TopN Files by Size"
$headerLine = " {0,4} {1,-60} {2,10}" -f "#", "File", "Size"
Write-ColorLine $headerLine White
Write-ColorLine " $Separator"
$files = Get-ChildItem -LiteralPath $Path -Recurse -File -ErrorAction SilentlyContinue |
Where-Object { $_.Length -ge $MinSizeBytes } |
Sort-Object Length -Descending |
Select-Object -First $TopN
$rank = 0
$result = @()
foreach ($file in $files) {
$rank++
$color = [ConsoleColor]::Gray
if ($file.Length -ge 1GB) { $color = [ConsoleColor]::Red }
elseif ($file.Length -ge 100MB) { $color = [ConsoleColor]::Yellow }
$displayPath = $file.FullName
if ($displayPath.Length -gt 58) { $displayPath = "..." + $displayPath.Substring($displayPath.Length - 55) }
$line = " {0,4} {1,-60} {2,10}" -f $rank, $displayPath, (Format-FileSize $file.Length)
Write-ColorLine $line $color
$result += [PSCustomObject]@{
Path = $file.FullName
SizeBytes = $file.Length
}
}
return $result
}
# ============================================================================
# OLD LARGE FILES
# ============================================================================
function Get-OldLargeFiles {
Write-Header "Old Large Files (> ${MinSizeMB} MB, older than $AgeWarnDays days)"
$headerLine = " {0,4} {1,-50} {2,10} {3,12}" -f "#", "File", "Size", "Last Modified"
Write-ColorLine $headerLine White
Write-ColorLine " $Separator"
$files = Get-ChildItem -LiteralPath $Path -Recurse -File -ErrorAction SilentlyContinue |
Where-Object { $_.Length -ge $MinSizeBytes -and $_.LastWriteTime -lt $AgeCutoff } |
Sort-Object Length -Descending |
Select-Object -First $TopN
if (-not $files -or $files.Count -eq 0) {
Write-ColorLine " No files found matching criteria."
return @()
}
$rank = 0
$result = @()
foreach ($file in $files) {
$rank++
$displayPath = $file.FullName
if ($displayPath.Length -gt 48) { $displayPath = "..." + $displayPath.Substring($displayPath.Length - 45) }
$line = " {0,4} {1,-50} {2,10} {3,12}" -f $rank, $displayPath, (Format-FileSize $file.Length), $file.LastWriteTime.ToString("yyyy-MM-dd")
Write-ColorLine $line Yellow
$result += [PSCustomObject]@{
Path = $file.FullName
SizeBytes = $file.Length
LastModified = $file.LastWriteTime.ToString("yyyy-MM-dd")
}
}
return $result
}
# ============================================================================
# SUMMARY
# ============================================================================
function Write-Summary {
param(
[int64]$TotalScanned,
[array]$OldFiles
)
Write-Header "Summary"
$oldCount = 0
$oldBytes = [int64]0
if ($OldFiles) {
$oldCount = $OldFiles.Count
$oldBytes = ($OldFiles | Measure-Object -Property SizeBytes -Sum).Sum
if ($null -eq $oldBytes) { $oldBytes = 0 }
}
Write-ColorLine (" Scan path: $Path")
Write-ColorLine (" Total scanned: $(Format-FileSize $TotalScanned)")
Write-ColorLine (" Min file size: ${MinSizeMB} MB")
Write-ColorLine (" Age threshold: $AgeWarnDays days")
Write-ColorLine ""
Write-ColorLine (" Old large files: $oldCount files")
Write-ColorLine (" Reclaimable space: $(Format-FileSize $oldBytes)") Yellow
Write-ColorLine ""
if ($oldBytes -gt 0) {
Write-ColorLine " -> Review old files above - candidates for cleanup or archival" Yellow
} else {
Write-ColorLine " OK - No old large files found" Green
}
Write-ColorLine ""
}
# ============================================================================
# JSON OUTPUT
# ============================================================================
function Get-JsonReport {
param(
[array]$Volumes,
[array]$TopDirs,
[array]$TopFilesList,
[array]$OldFiles,
[int64]$TotalScanned
)
$oldBytes = [int64]0
if ($OldFiles) {
$oldBytes = ($OldFiles | Measure-Object -Property SizeBytes -Sum -ErrorAction SilentlyContinue).Sum
if ($null -eq $oldBytes) { $oldBytes = 0 }
}
$report = [PSCustomObject]@{
scan_path = $Path
timestamp = (Get-Date -Format "yyyy-MM-ddTHH:mm:ssZ")
min_size_mb = $MinSizeMB
age_warn_days = $AgeWarnDays
max_depth = $MaxDepth
volumes = @($Volumes)
top_directories = @($TopDirs)
top_files = @($TopFilesList)
old_large_files = @($OldFiles)
summary = [PSCustomObject]@{
total_scanned_bytes = $TotalScanned
old_file_count = if ($OldFiles) { $OldFiles.Count } else { 0 }
reclaimable_bytes = $oldBytes
}
}
return $report | ConvertTo-Json -Depth 5
}
# ============================================================================
# MAIN
# ============================================================================
if (-not (Test-Path -LiteralPath $Path)) {
Write-Error "Path does not exist: $Path"
exit 1
}
# Calculate total size of scanned path
$totalScanned = [int64]0
try {
$allFiles = Get-ChildItem -LiteralPath $Path -Recurse -File -ErrorAction SilentlyContinue
$totalScanned = ($allFiles | Measure-Object -Property Length -Sum -ErrorAction SilentlyContinue).Sum
if ($null -eq $totalScanned) { $totalScanned = 0 }
} catch {
$totalScanned = 0
}
if ($Json) {
# Collect data silently for JSON - redirect console output
$volumes = @()
try {
$disks = Get-CimInstance -ClassName Win32_LogicalDisk -Filter "DriveType=3" -ErrorAction SilentlyContinue
} catch {
$disks = Get-WmiObject -Class Win32_LogicalDisk -Filter "DriveType=3" -ErrorAction SilentlyContinue
}
foreach ($disk in $disks) {
$total = [int64]$disk.Size
$free = [int64]$disk.FreeSpace
if ($total -eq 0) { continue }
$used = $total - $free
$pct = [math]::Round(($used / $total) * 100, 1)
$volumes += [PSCustomObject]@{
Drive = $disk.DeviceID
Label = $disk.VolumeName
SizeBytes = $total
FreeBytes = $free
UsedPct = $pct
}
}
$topDirs = Get-DirectorySizeRecursive -DirPath $Path -CurrentDepth 1 -Limit $MaxDepth |
Sort-Object SizeBytes -Descending | Select-Object -First $TopN
$topFilesList = Get-ChildItem -LiteralPath $Path -Recurse -File -ErrorAction SilentlyContinue |
Where-Object { $_.Length -ge $MinSizeBytes } |
Sort-Object Length -Descending |
Select-Object -First $TopN |
ForEach-Object { [PSCustomObject]@{ Path = $_.FullName; SizeBytes = $_.Length } }
$oldFiles = Get-ChildItem -LiteralPath $Path -Recurse -File -ErrorAction SilentlyContinue |
Where-Object { $_.Length -ge $MinSizeBytes -and $_.LastWriteTime -lt $AgeCutoff } |
Sort-Object Length -Descending |
Select-Object -First $TopN |
ForEach-Object { [PSCustomObject]@{ Path = $_.FullName; SizeBytes = $_.Length; LastModified = $_.LastWriteTime.ToString("yyyy-MM-dd") } }
Get-JsonReport -Volumes $volumes -TopDirs $topDirs -TopFilesList $topFilesList -OldFiles $oldFiles -TotalScanned $totalScanned
exit 0
}
# Interactive output
Write-ColorLine ""
Write-ColorLine "Disk Usage Report" White
Write-ColorLine ("{0} - Scanning: {1}" -f (Get-Date -Format "yyyy-MM-dd HH:mm:ss"), $Path)
$volumes = Get-VolumeOverview
$topDirs = Get-TopDirectories
$topFiles = Get-TopFiles
$oldFiles = Get-OldLargeFiles
Write-Summary -TotalScanned $totalScanned -OldFiles $oldFiles