Servers - Удаление старых файлов (версия 2)

Реклама:
Записка от 20.02.2017

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

Вверх