В процессе работы сервера или компьютера на дисках накапливаются различные файлы, которые занимают полезное пространство, а иногда даже могут привести к остановке работы операционной системы из-за нехватки свободного места. По этой причине время от времени необходимо очищать папки с временными и журнальными файлами. Чтобы избежать ручной работы, я рекомендую использовать скрипты, которые можно поставить в планировщик 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).
