适用于 PowerShell 5.1 及以上版本
磁盘空间不足是运维中最常见的问题之一。日志文件堆积、临时文件未清理、回收站膨胀、Windows 更新缓存占用等问题,如果不加以管理,迟早会导致系统异常甚至服务中断。在容器化和虚拟化环境中,磁盘资源更是需要精打细算,自动化的磁盘清理机制必不可少。
手动清理磁盘既低效又容易遗漏,而且无法做到定期执行。通过 PowerShell 脚本可以精确定位各类可清理的目标,量化空间占用,并在安全的前提下自动执行清理操作。本文将介绍如何构建一个全面的磁盘清理自动化方案。
磁盘空间分析
在执行清理之前,需要先了解磁盘空间的使用情况以及哪些目录占用最多。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39
| function Get-DiskSpaceInfo {
[CmdletBinding()] param( [string]$DriveLetter )
$drives = Get-CimInstance -ClassName Win32_LogicalDisk -Filter 'DriveType = 3'
if ($DriveLetter) { $drives = $drives | Where-Object { $_.DeviceID -eq "$($DriveLetter):" } }
foreach ($drive in $drives) { $totalGB = [math]::Round($drive.Size / 1GB, 2) $freeGB = [math]::Round($drive.FreeSpace / 1GB, 2) $usedGB = [math]::Round(($drive.Size - $drive.FreeSpace) / 1GB, 2) $usedPercent = [math]::Round(($drive.Size - $drive.FreeSpace) / $drive.Size * 100, 1)
[PSCustomObject]@{ Drive = $drive.DeviceID TotalGB = $totalGB UsedGB = $usedGB FreeGB = $freeGB UsedPercent = $usedPercent Status = if ($usedPercent -gt 90) { 'Critical' } elseif ($usedPercent -gt 80) { 'Warning' } else { 'OK' } } } }
Get-DiskSpaceInfo | Format-Table -AutoSize
|
执行结果示例:
1 2 3 4
| Drive TotalGB UsedGB FreeGB UsedPercent Status ----- ------- ------ ------ ----------- ------ C: 100 78.5 21.5 78.5 Warning D: 500 210.3 289.7 42.1 OK
|
分析目录空间占用
找出哪些目录占用空间最多,为清理提供依据。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60
| function Get-FolderSize {
[CmdletBinding()] param( [Parameter(Mandatory)] [string]$Path,
[int]$Depth = 1 )
if (-not (Test-Path $Path)) { Write-Warning "路径不存在:$Path" return }
$results = [System.Collections.Generic.List[PSObject]]::new()
$rootFiles = Get-ChildItem -Path $Path -File -Recurse -Force -ErrorAction SilentlyContinue $rootSize = ($rootFiles | Measure-Object -Property Length -Sum).Sum
$subDirs = Get-ChildItem -Path $Path -Directory -Force -ErrorAction SilentlyContinue
foreach ($dir in $subDirs) { $files = Get-ChildItem -Path $dir.FullName -File -Recurse -Force -ErrorAction SilentlyContinue $size = ($files | Measure-Object -Property Length -Sum).Sum $fileCount = ($files | Measure-Object).Count
$results.Add([PSCustomObject]@{ Folder = $dir.Name SizeMB = [math]::Round($size / 1MB, 2) FileCount = $fileCount FullPath = $dir.FullName }) }
$results | Sort-Object SizeMB -Descending }
$commonPaths = @( @{ Name = '临时文件'; Path = $env:TEMP } @{ Name = 'Windows 临时'; Path = 'C:\Windows\Temp' } @{ Name = '回收站'; Path = 'C:\`$Recycle.Bin' } @{ Name = 'Windows 更新'; Path = 'C:\Windows\SoftwareDistribution' } )
foreach ($item in $commonPaths) { Write-Host "`n--- $($item.Name) ---" -ForegroundColor Cyan Get-FolderSize -Path $item.Path | Select-Object -First 5 | Format-Table -AutoSize }
|
执行结果示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| --- 临时文件 ---
Folder SizeMB FileCount FullPath ------ ------ --------- -------- Logs 512.3 1245 C:\Users\admin\AppData\Local\Temp\Logs Cache 256.8 890 C:\Users\admin\AppData\Local\Temp\Cache Extraction 128.1 45 C:\Users\admin\AppData\Local\Temp\Extraction
--- Windows 临时 ---
Folder SizeMB FileCount FullPath ------ ------ --------- -------- UpdateStaging 890.5 3200 C:\Windows\Temp\UpdateStaging InstallCache 345.2 678 C:\Windows\Temp\InstallCache
|
清理临时文件
临时文件是最常见的空间占用来源。以下函数可以安全清理各类临时文件。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77
| function Remove-TempFiles {
[CmdletBinding(SupportsShouldProcess)] param( [Parameter(Mandatory)] [string[]]$Path,
[int]$MaxAge = 7,
[string[]]$Extensions,
[switch]$Force )
$cutoffDate = (Get-Date).AddDays(-$MaxAge) $totalSize = 0 $totalFiles = 0
foreach ($targetPath in $Path) { if (-not (Test-Path $targetPath)) { Write-Verbose "路径不存在,跳过:$targetPath" continue }
Write-Verbose "扫描目录:$targetPath"
$files = Get-ChildItem -Path $targetPath -File -Recurse -Force -ErrorAction SilentlyContinue | Where-Object { $_.LastWriteTime -lt $cutoffDate }
if ($Extensions) { $files = $files | Where-Object { $Extensions -contains $_.Extension.ToLower() } }
foreach ($file in $files) { $totalSize += $file.Length $totalFiles++
if ($PSCmdlet.ShouldProcess($file.FullName, '删除文件')) { try { Remove-Item -Path $file.FullName -Force -ErrorAction Stop Write-Verbose "已删除:$($file.FullName)" } catch { Write-Warning "删除失败:$($file.FullName) - $($_.Exception.Message)" } } } }
[PSCustomObject]@{ FilesDeleted = $totalFiles SpaceFreedMB = [math]::Round($totalSize / 1MB, 2) SpaceFreedGB = [math]::Round($totalSize / 1GB, 2) CutoffDate = $cutoffDate } }
$result = Remove-TempFiles -Path @( $env:TEMP 'C:\Windows\Temp' ) -MaxAge 7 -Verbose
Write-Host "`n清理结果:" $result | Format-List
|
执行结果示例:
1 2 3 4 5 6 7 8 9 10 11
| 详细: 扫描目录:C:\Users\admin\AppData\Local\Temp 详细: 已删除:C:\Users\admin\AppData\Local\Temp\Logs\app-20250810.log 详细: 已删除:C:\Users\admin\AppData\Local\Temp\Cache\data-20250809.tmp 详细: 扫描目录:C:\Windows\Temp 详细: 已删除:C:\Windows\Temp\UpdateStaging\patch-20250811.cab
清理结果: FilesDeleted : 2456 SpaceFreedMB : 1024.5 SpaceFreedGB : 1.0 CutoffDate : 2025/8/13 8:00:00
|
清理日志文件
日志文件是另一个重要的空间占用来源,需要按策略进行归档和清理。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86
| function Remove-OldLogFiles {
[CmdletBinding(SupportsShouldProcess)] param( [Parameter(Mandatory)] [string]$LogPath,
[int]$MaxAge = 30,
[string]$ArchivePath,
[string]$Pattern = '*.log' )
if (-not (Test-Path $LogPath)) { throw "日志目录不存在:$LogPath" }
$cutoffDate = (Get-Date).AddDays(-$MaxAge) $files = Get-ChildItem -Path $LogPath -Filter $Pattern -File -Force | Where-Object { $_.LastWriteTime -lt $cutoffDate }
$stats = @{ Archived = 0 Deleted = 0 TotalSizeMB = 0 }
foreach ($file in $files) { $stats['TotalSizeMB'] += $file.Length / 1MB
if ($ArchivePath) { if (-not (Test-Path $ArchivePath)) { New-Item -Path $ArchivePath -ItemType Directory -Force | Out-Null }
$zipName = "$($file.BaseName)-$(Get-Date -Format 'yyyyMMdd').zip" $zipPath = Join-Path $ArchivePath $zipName
if ($PSCmdlet.ShouldProcess($file.FullName, "归档到 $zipPath")) { Compress-Archive -Path $file.FullName -DestinationPath $zipPath -Force Remove-Item -Path $file.FullName -Force $stats['Archived']++ Write-Verbose "已归档:$($file.Name) -> $zipName" } } else { if ($PSCmdlet.ShouldProcess($file.FullName, '删除')) { Remove-Item -Path $file.FullName -Force $stats['Deleted']++ Write-Verbose "已删除:$($file.Name)" } } }
[PSCustomObject]@{ LogPath = $LogPath MaxAge = $MaxAge CutoffDate = $cutoffDate FilesCount = $files.Count Archived = $stats['Archived'] Deleted = $stats['Deleted'] SizeMB = [math]::Round($stats['TotalSizeMB'], 2) } }
$logResult = Remove-OldLogFiles ` -LogPath 'C:\inetpub\logs\LogFiles\W3SVC1' ` -MaxAge 30 ` -ArchivePath 'D:\Archive\IISLogs' ` -Verbose
$logResult | Format-List
|
执行结果示例:
1 2 3 4 5 6 7 8 9 10 11
| 详细: 已归档:u_ex250715.log -> u_ex250715-20250820.zip 详细: 已归档:u_ex250716.log -> u_ex250716-20250820.zip 详细: 已归档:u_ex250717.log -> u_ex250717-20250820.zip
LogPath : C:\inetpub\logs\LogFiles\W3SVC1 MaxAge : 30 CutoffDate : 2025/7/21 8:00:00 FilesCount : 15 Archived : 15 Deleted : 0 SizeMB : 2048.5
|
清理 Windows 更新缓存和系统文件
Windows 更新下载的文件和系统组件缓存往往占用大量空间。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51
| function Remove-WindowsUpdateCache {
[CmdletBinding(SupportsShouldProcess)] param()
$wuService = 'wuauserv'
Write-Verbose "停止 Windows Update 服务..." Stop-Service -Name $wuService -Force -ErrorAction SilentlyContinue
$cachePaths = @( 'C:\Windows\SoftwareDistribution\Download' 'C:\Windows\SoftwareDistribution\DataStore' )
$totalFreed = 0
foreach ($path in $cachePaths) { if (Test-Path $path) { $files = Get-ChildItem -Path $path -Recurse -Force -ErrorAction SilentlyContinue $size = ($files | Measure-Object -Property Length -Sum).Sum $totalFreed += $size
if ($PSCmdlet.ShouldProcess($path, '清理目录内容')) { Get-ChildItem -Path $path -Force | Remove-Item -Recurse -Force -ErrorAction SilentlyContinue Write-Verbose "已清理:$path ($([math]::Round($size / 1MB, 2)) MB)" } } }
Write-Verbose "启动 Windows Update 服务..." Start-Service -Name $wuService -ErrorAction SilentlyContinue
[PSCustomObject]@{ CacheCleaned = $cachePaths SpaceFreedMB = [math]::Round($totalFreed / 1MB, 2) ServiceState = (Get-Service -Name $wuService).Status } }
$updateResult = Remove-WindowsUpdateCache -Verbose $updateResult | Format-List
|
执行结果示例:
1 2 3 4 5 6 7 8
| 详细: 停止 Windows Update 服务... 详细: 已清理:C:\Windows\SoftwareDistribution\Download (3456.78 MB) 详细: 已清理:C:\Windows\SoftwareDistribution\DataStore (234.5 MB) 详细: 启动 Windows Update 服务...
CacheCleaned : {C:\Windows\SoftwareDistribution\Download, C:\Windows\SoftwareDistribution\DataStore} SpaceFreedMB : 3691.28 ServiceState : Running
|
综合清理工作流
将各类清理操作整合为一个可定期执行的综合脚本。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97
| function Invoke-DiskCleanup {
[CmdletBinding()] param( [string]$DriveLetter = 'C',
[int]$TempFileMaxAge = 7,
[int]$LogFileMaxAge = 30,
[switch]$CleanUpdateCache )
Write-Host "===== 磁盘清理开始 =====" -ForegroundColor Cyan Write-Host "目标驱动器:$($DriveLetter):" Write-Host "执行时间:$(Get-Date -Format 'yyyy-MM-dd HH:mm:ss')"
$before = Get-DiskSpaceInfo -DriveLetter $DriveLetter Write-Host "`n清理前可用空间:$($before.FreeGB) GB" -ForegroundColor Yellow
$results = [System.Collections.Generic.List[PSObject]]::new()
Write-Host "`n[1/4] 清理临时文件..." -ForegroundColor Cyan $tempResult = Remove-TempFiles -Path @( $env:TEMP 'C:\Windows\Temp' ) -MaxAge $TempFileMaxAge $results.Add($tempResult) Write-Host " 已删除 $($tempResult.FilesDeleted) 个文件,释放 $($tempResult.SpaceFreedMB) MB"
Write-Host "`n[2/4] 清理日志文件..." -ForegroundColor Cyan $logPaths = @( 'C:\inetpub\logs\LogFiles' 'C:\Windows\Logs' ) foreach ($logPath in $logPaths) { if (Test-Path $logPath) { $logResult = Remove-OldLogFiles -LogPath $logPath -MaxAge $LogFileMaxAge $results.Add($logResult) Write-Host " 清理 $logPath - $($logResult.SizeMB) MB" } }
Write-Host "`n[3/4] 清空回收站..." -ForegroundColor Cyan try { Clear-RecycleBin -DriveLetter $DriveLetter -Force -ErrorAction Stop Write-Host " 回收站已清空" } catch { Write-Warning "清空回收站失败:$_" }
if ($CleanUpdateCache) { Write-Host "`n[4/4] 清理 Windows 更新缓存..." -ForegroundColor Cyan $wuResult = Remove-WindowsUpdateCache $results.Add($wuResult) Write-Host " 释放 $($wuResult.SpaceFreedMB) MB" } else { Write-Host "`n[4/4] 跳过 Windows 更新缓存清理" -ForegroundColor Gray }
$after = Get-DiskSpaceInfo -DriveLetter $DriveLetter $freedGB = [math]::Round($after.FreeGB - $before.FreeGB, 2)
Write-Host "`n===== 清理完成 =====" -ForegroundColor Green Write-Host "清理前可用空间:$($before.FreeGB) GB" Write-Host "清理后可用空间:$($after.FreeGB) GB" Write-Host "释放空间:$freedGB GB" -ForegroundColor Green
[PSCustomObject]@{ Drive = "$($DriveLetter):" BeforeGB = $before.FreeGB AfterGB = $after.FreeGB FreedGB = $freedGB Results = $results ExecutedAt = Get-Date } }
$cleanupReport = Invoke-DiskCleanup -DriveLetter 'C' -TempFileMaxAge 7 -LogFileMaxAge 30 -CleanUpdateCache
|
执行结果示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| ===== 磁盘清理开始 ===== 目标驱动器:C: 执行时间:2025-08-20 08:00:00
清理前可用空间:21.5 GB
[1/4] 清理临时文件... 已删除 2456 个文件,释放 1024.5 MB
[2/4] 清理日志文件... 清理 C:\inetpub\logs\LogFiles - 2048.5 MB 清理 C:\Windows\Logs - 128.3 MB
[3/4] 清空回收站... 回收站已清空
[4/4] 清理 Windows 更新缓存... 释放 3691.28 MB
===== 清理完成 ===== 清理前可用空间:21.5 GB 清理后可用空间:27.27 GB 释放空间:5.77 GB
|
注意事项
- 备份优先:清理前务必备份重要的日志和配置文件,特别是生产环境的 IIS 日志、应用程序日志等,确认不再需要后再清理。
- 文件锁定:正在使用的文件无法删除,脚本中应使用
-ErrorAction SilentlyContinue 跳过这些文件,避免因个别文件锁定导致整个脚本中断。
- 服务依赖:清理 Windows 更新缓存需要停止
wuauserv 服务,如果组织有补丁管理窗口,应在窗口外执行此操作,避免影响正常更新流程。
- 测试先行:在生产环境运行清理脚本前,先使用
-WhatIf 参数进行干运行(dry run),确认清理范围符合预期后再实际执行。
- 定期执行:建议将清理脚本配置为计划任务,每周或每月自动执行,避免一次性积累过多文件导致清理时间过长。
- 监控告警:配合磁盘空间监控(如当可用空间低于 20% 时告警),形成”监控-告警-清理”的闭环管理,避免磁盘空间耗尽导致的系统故障。