В процессе работы сервера или компьютера на дисках накапливаются различные файлы, которые занимают полезное пространство, а иногда даже могут привести к остановке работы операционной системы из-за нехватки свободного места. По этой причине время от времени необходимо очищать папки с временными и журнальными файлами. Чтобы избежать ручной работы, я рекомендую использовать скрипты, которые можно поставить в планировщик Windows и проводить автоматическую чистку.
В статье "Примеры скриптов для администрирования" я уже публиковал VBS-скрипт, который вычищает папки на серверах Windows Server 2008. В этой заметке я приведу пример Powershell-скрипта для очистки папок на сервере Windows Server 2012 (хотя на этой версии операционной системы старый VBS-скрипт работает так же, как и на старой).
# Powershell 3.0 or later is required # Does not work at SYSTEM account in Windows 2008 R2 # --- Declare variables --- $FoldersToPurge = New-Object System.Collections.ArrayList # --- Input data --- [void] $FoldersToPurge.Add( @{ "Path" = 'C:\Windows\Temp'; "Subfolders" = $True; "Mask" = '*'; "Exclude" = $Nothing; "DaysOld" = 60 } ) [void] $FoldersToPurge.Add( @{ "Path" = 'D:\RDSProfiles\RDS_SLQTools'; "Subfolders" = $False; "Mask" = '*'; "Exclude" = @("UVHD-template.vhdx"); "DaysOld" = 90 } ) # --- Functions --- Function Write-ScriptLog { Param( [CmdletBinding()] [Parameter(ValueFromPipeline=$true,ValueFromPipelineByPropertyName=$true)] [String]$Message, [Parameter(ValueFromPipeline=$true,ValueFromPipelineByPropertyName=$true)] [String]$LogFile ) Process { $LogMessage = Get-Date -uformat "%d.%m.%Y %H:%M:%S" $LogMessage += "`t" $LogMessage += $Message $LogMessage | Out-File -FilePath $LogFile -Append } }#End Function Function Get-FolderSize { Param( [CmdletBinding()] [Parameter(ValueFromPipeline=$true,ValueFromPipelineByPropertyName=$true)] [String]$Path ) Begin { $oFSO = New-Object -comobject Scripting.FileSystemObject } Process{ $oFolder = $oFSO.GetFolder($Path) Return ($oFolder.Size) } } # End Function Function Purge-Folder { Param( [CmdletBinding()] [Parameter(ValueFromPipeline=$true,ValueFromPipelineByPropertyName=$true)] [String]$Path, [Parameter(ValueFromPipeline=$true,ValueFromPipelineByPropertyName=$true)] [Boolean]$Subfolders, [Parameter(ValueFromPipeline=$true,ValueFromPipelineByPropertyName=$true)] [String]$Mask, [Parameter(ValueFromPipeline=$true,ValueFromPipelineByPropertyName=$true)] [String[]]$Exclude, [Parameter(ValueFromPipeline=$true,ValueFromPipelineByPropertyName=$true)] [int]$DaysOld ) Process { If ($DaysOld -lt 1) { Write-ScriptLog -LogFile $LogFile -Message ("Error: " + $DaysOld + " days is set for folder " + $Path) Return } If (Test-Path $Path -PathType Container) { Write-ScriptLog -LogFile $LogFile -Message ("Purging " + $Path + ` " folder by deleing content which is older then " + $DaysOld + " days") # Получение коллекции файлов $Files= Get-ChildItem ($Path+"\*") -Force -File -Include $Mask # Обработка каждого файла из коллекции ForEach ($CurrentFile in $Files) { $CurrentFileWriteDate = $CurrentFile.LastWriteTime $CurrentFileCreateDate = $CurrentFile.CreationTime $CurrentFileOld_c = ((Get-Date) - $CurrentFileCreateDate).Days $CurrentFileOld_w = ((Get-Date) - $CurrentFileWriteDate).Days # Проверка, является ли файл устаревшим If (($CurrentFileOld_c -gt $DaysOld) -and ($CurrentFileOld_w -gt $DaysOld)) { $SkipFlag = $False ForEach ($ExcludeFile in $Exclude) { If ($CurrentFile.Name -eq $ExcludeFile) { $SkipFlag = $True } } If (-Not($SkipFlag)) { Write-ScriptLog -LogFile $LogFile -Message ("--> Delete " + $CurrentFile.FullName + ` " (Created " + $CurrentFileCreateDate.ToString("dd.MM.yyyy") + ")") # Удаление устаревшего файла Try { Remove-Item $CurrentFile.FullName -Force -ErrorAction Stop } Catch { Write-ScriptLog -LogFile $LogFile -Message ("----> Error: " + $_.Exception.Message) } } Else { Write-ScriptLog -LogFile $LogFile -Message ("--> Skip " + $CurrentFile.FullName + ` " (Created " + $CurrentFileCreateDate.ToString("dd.MM.yyyy") + ")") } Start-Sleep -Milliseconds 20 } } If ($Subfolders) { # Получение коллекции подпапок $Folders= Get-ChildItem $Path -Force -Directory # Обработка каждой подпапки ForEach ($CurrentFolder in $Folders) { # Рекурсивный вызов процедуры удаления старых файлов - подпрограмма вызывает сама себя Purge-Folder -Path $CurrentFolder.FullName -Subfolders $True -Mask $Mask -Exclude $Exclude ` -DaysOld $DaysOld # Проверка размера папки $CurrentFolderSize = Get-FolderSize -Path $CurrentFolder.FullName $CurrentFolderCreationDate = $CurrentFolder.CreationTime $CurrentFolderOld_c = ((Get-Date) - $CurrentFolderCreationDate).Days If (($CurrentFolderSize -eq 0) -and ($CurrentFolderOld_c -gt $DaysOld)) { Write-ScriptLog -LogFile $LogFile -Message ("--> Delete " + $CurrentFolder.FullName + ` " (Created " + $CurrentFolderCreationDate.ToString("dd.MM.yyyy") + ")") # Удаление пустой папки Try { Remove-Item $CurrentFolder.FullName -Force -Confirm:$False -ErrorAction Stop } Catch { Write-ScriptLog -LogFile $LogFile -Message ("----> Error: " + $_.Exception.Message) } } } } } Else { Write-ScriptLog -LogFile $LogFile -Message ("Error: " + $Path + " folder does not exist") Return } } } # End Function # --- Start --- $ScriptName = $MyInvocation.MyCommand.Name $ScriptFolder = $MyInvocation.MyCommand.Path.SubString(0,($MyInvocation.MyCommand.Path.Length - ` $MyInvocation.MyCommand.Name.Length)) $LogFile = $ScriptFolder + 'Logs\' + (Get-Date -format yyyy_MM_dd) + "_" + ` $MyInvocation.MyCommand.Name.SubString(0,($MyInvocation.MyCommand.Name.Length - 4)) + ".log" $WindowsFolder = $env:windir $WinTempFolder = $WindowsFolder + "\Temp" # Log-file creation If (-not(Test-Path ($ScriptFolder + 'Logs') -PathType Container )) { New-Item -ItemType Directory -Path ($ScriptFolder + 'Logs') } Out-File -FilePath $LogFile Write-ScriptLog -LogFile $LogFile -Message ("Start " + $ScriptName) Write-ScriptLog -LogFile $LogFile -Message ("===================================") # Purge folders ForEach ($CurrentFolderToPurge in $FoldersToPurge) { Write-ScriptLog -LogFile $LogFile -Message ("===== Processing " + ` $CurrentFolderToPurge.Path + " folder =====") Purge-Folder -Path $CurrentFolderToPurge.Path -Subfolders $CurrentFolderToPurge.Subfolders ` -Mask $CurrentFolderToPurge.Mask -Exclude $CurrentFolderToPurge.Exclude ` -DaysOld $CurrentFolderToPurge.DaysOld } Write-ScriptLog -LogFile $LogFile -Message ("===================================") Write-ScriptLog -LogFile $LogFile -Message ("Stop " + $ScriptName)
Использование сценария: в разделе --- Input data --- необходимо ввести секцию добавления элемента к массиву FoldersToPurge, где указать путь к очищаемой папке Path, флаг обработки подпапок Subfolders, маску обработки файлов Mask, список исключений Exclude (одномерный массив из имен файлов), период устаревания DaysOld в днях.
Если флаг обработки подпапок Subfolders установлен в $True, то удаление происходит по рекурсивному методу - сценарий просматривает дату создания каждого файла в папке и удаляет старые файлы, а так же обрабатывает все подпапки текущей папки по тому же принципу.
Указанный скрипт рекомендуется поставить в планировщик Windows на ежедневное выполнение в ночные часы, когда на серверах нет пользователей. Запуск задания нужно выполнять от имени системы (от имени пользователя SYSTEM).