Remove-OldFiles

Es gibt viele Prozesse, die ihre Tätigkeit in Textdateien protokollieren. Exchange selbst nutzt diese Methode intensiv aber Exchange räumt auch auf. Fast alle Logdateien können über eine Verzeichnisgröße und Dateialter limitiert werden, so dass Exchange selbst alte Dateien wieder aufräumt.

Es gibt aber auch Prozesse wie z.B. der Webserver (IIS) oder Syslog-Daemon und das ein oder andere selbstgeschriebene Skript, welches seine Tätigkeit auch in Logdateien protokolliert und wenn niemand aufpasst, dann werden die Verzeichnisse und Festplatten voller und voller. Neben dem Platz ist auch das Thema Datenschutz nicht ganz unwichtig. Zwar wird auf politischer Ebene immer wieder über eine "Vorratsdatenspeicherung" diskutiert aber im gewerblichen Bereich ist eine Datensammlung von nicht mehr relevanten Daten nicht immer zulässig. Sie sparen also nicht nur Festplattenplatz, sondern die in den Logdateien enthaltenen Informationen können nicht von unberechtigten Personen eingesehen werden, wenn diese nicht mehr vorliegen.

Leider unterstützt weder NTFS noch Windows eine Alterung von Dateien und Verzeichnissen. Aber es gibt genug Beispiele und Programme, wie solche Dateien automatisiert gelöscht werden können.

Der "One-Liner"

Dieses kleine Beispiel ist bei einem Kunden entstanden, der alte Dateien eines Jobprotokolls automatisch gelöscht haben wollte. Erst hatte ich an VBScript gedacht aber eigentlich ist es ein PowerShell-Einzeiler, den ich nicht vorenthalten möchte. Er zeigt wunderbar die Leistungsfähigkeit von PowerShell durch die Verkettung von Befehlen diese Aufgabe zu meistern.

Get-ChildItem $path | `
    where {$_.lastwritetime -lt (get-date).adddays(-$maxdays)} | `
        Remove-Item

Zur Lesbarkeit wurde der Code umgebrochen. Diese kleine Zeile können Sie direkt in ihr Skript addieren, um frühere Protokolldateien gleich wieder entfernen zu lassen.

Wenn der erzeugende Prozess nicht von ihnen selbst als PowerShell-Skript entworfen wurde, dann können Sie das Skript natürlich auch über den Taskplaner unbeaufsichtigt starten lassen.

Wichtig:
Das Skript löscht alle Dateien im Pfad "$path", die älter als "$maxdays" sind. Das macht natürlich nur Sinn, wenn in dem angegebenen Pfad nur Logdateien liegen. Ansonsten sollten Sie bei Get-Childitem die Dateinamen mit Wildcards eingrenzen.

PS1-Script

Wer alte Dateien per Taskplaner löschen möchte, kann diese Zeile natürlich auch als PS1-Datei abspeichern. Hier bietet es sich an, die Funktion selbst allerdings ebenfalls zu überwachen, z.B.: durch Einträge im Eventlog oder den Versand einer Mail am Ende. Auch macht es dann Sinn, einige Einstellungen einfach per Parameter zu übergeben. Damit erweitert sich natürlich etwas der Code:

# remove-OldFiles

param (
    [string]$path = "C:\inetpub\logs\LogFiles\W3SVC1",
    [long]$maxdays = 7,
    [string]$smtpto = "frank.carius@netatwork.de",
    [string]$smtpserver="mail.netatwork.de"
)

$Error.clear()
Get-ChildItem $path -file -recurse `
   | where {$_.lastwritetime -lt (get-date).adddays(-$maxdays)} `
   | Remove-Item
if ($error) {
   write-host "Error"
   $subject = "Remove-OldIISlog Failed on Server: "+ $env:computername + "Path:" + $path
   $body = [string]$error
}
else {
   write-host "ok"
   $subject = "Remove-OldIISlog OK on Server: "+ $env:computername + "Path:" + $path
   $body ="Old Files deleted succesfully"
}

send-mailmessage `
   -to $smtpto `
   -smtpserver $smtpserver `
   -from (($env:computername)+"@netatwork.de") `
   -Subject $subject `
   -body $body

Wer die Funktion nicht immer aufrufen will, kann daraus natürlich auch ein PSModule machen, welches dann einfach eingebunden wird.

Rekursion und Verzeichnisse

Wenn Sie damit nicht nur einzelne Dateien sondern ganze Verzeichnisse entfernen wollen, dann müssen Sie bei Remove-Item natürlich noch Optionen angeben:

Remove-Item `
   -force `
   -recursive

Komische Fehler und Error-Abhandlung

Die obigen Codes sehen einfach und logisch aus. Ich nutze sie gerne in automatisierten Skripten, um eben alte Protokolldateien zu entfernen. Wenn ich nach dem Code aber z.B. am Ende meines Skripte die Variable $ERROR abfrage, dann findet sich da manchmal doch der ein oder andere Fehler. Anscheinend kann es passieren, dass "Remove-Item" ein Element nicht entfernen kann. Die genaue Ursache hat sich mir noch nicht erschlossen, das das gleiche Skript ein paar Sekunden später die Datei dann problemlos löscht. Ich kann mir aktuell nur zwei Dinge vorstellen:

  • Virenscanner
    Eventuell ist der Virenscanner mit schuld daran, der erst noch mal was scannen will und daher den Zugriff verhindert.
  • Pipeline und Parallele Verarbeitung
    Eventuell liegt es aber auch an der Pipeline, dass Get-ChildItem das Verzeichnis einliest und parallel "Remove-Item" durch das Löschen das Einlesen der Liste stört.

Ich habe daher eine abgewandelte Version gebaut, die zuerst einmal die Liste der zu löschenden Dateien erstellt und den "FullName" als Liste von Strings erstellt und erst dann die Dateien löscht.

[string[]]$oldlogfilelist=""
get-item -path "C:\Tasks\skriptname\logs\*.log" `
   | where {$_.lastwritetime -lt ((get-date).adddays(-30))} `
   | %{$oldlogfilelist+=$_.fullname}
foreach ($oldlogfile in $oldlogfilelist) {
    if ($oldlogfile) { # only if oldlogpath is not null or empty
        write-host "  Removing Old LogFile:" $oldlogfile
        remove-item -path $oldlogfile
    }
} 

Ich werde bei Gelegenheit diesen Abschnitt aktualisieren, wenn ich weitere Erfahrungen damit gesammelt habe.

Andere Verwertung

Natürlich müssen Sie mit dem Skript nichts löschen. Sie können genauso einfach mit dem Skript die neuen oder vor kurzem veränderten Dateien ermitteln und Aktionen auslösen.

Weitere Links