2016-06-28 6 views
3

Ich muss einige Dateien programmgesteuert (vorzugsweise mit Powershell) aus einem Ordner löschen, die älter als eine bestimmte Anzahl von Tagen sind.Löschen von Dateien älter als xx Tage

Ich habe ein einfaches Skript geschrieben, das dies tun wird, aber das Problem, das ich habe, ist, dass es Probleme zu haben scheint, sogar zu löschen, aufgrund der schiere Menge an Dateien im Ordner.

Ich suche nach einer Möglichkeit, in Stapeln wahrscheinlich zu löschen. So würde es vielleicht die ersten 1000 bekommen, löschen und dann weiter so.

Im Moment hat der Ordner wahrscheinlich mehrere hunderttausend Dateien und ist praktisch nicht zu durchqueren.

Param(
    [Parameter(Mandatory=$true)][string]$Path, 
    [Parameter(Mandatory=$true)][string]$DaysToDelete 
) 

$limit = (Get-Date).AddDays($DaysToDelete) 
$LogFile = "FileCleanupLog-$(Get-Date -f yyyyMMdd_HH_mm_ss).txt"; 

function Log-Message 
{ 
    Param ([string]$logtext) 
    Add-content $LogFile -value $logtext 
} 

If (-Not (Test-Path $Path)) 
{ 
    Write-Host "Invalid Path provided!" -ForegroundColor Red 
    Exit 
} 

$files = Get-ChildItem -Path $Path -Recurse -Force | Where-Object { !$_.PSIsContainer -and $_.CreationTime -lt $limit } 

If ($files.Count -GT 1) { 
    $files | % {$directory=$_.DirectoryName;(Log-Message "Deleting File $directory\$_");$_ } | Remove-Item -Force 
} 
+0

Haben Sie versucht, Piping get-childitem zu SELECT, wie in 'get-childitem. | Wählen Sie -first 100' aus? Sie müssen das Skript, das Sie derzeit haben, veröffentlichen. –

+0

Danke, dass du so unhöflich bist. Wenn Sie sich meine Frage ansehen, habe ich gesagt, ich suche nach einer Möglichkeit, Dateien in Stapeln zu löschen. – TaylorN

+0

_Ich habe ein einfaches Skript geschrieben, das das tut_ Vielleicht solltest du das einbeziehen, was du bisher versucht hast. Gerade jetzt liest sich das wie ein Gimmie die Codez-Frage. In den meisten Fällen sind wir hier, um den bestehenden Code zu reparieren oder zu verbessern. Selbst wenn Sie es in "Chargen" getan hätten, würden Sie es immer noch einzeln tun, es sei denn, Sie hätten Jobs oder etwas verwendet. Suchen Sie nach Fortschrittsanzeige?Was ist der Schaden, wenn man es laufen lässt und sein Ding macht? Wenn es oft genug gemacht wird, wird nur der erste Durchgang lutschen. Ich habe vor kurzem eine Aufgabe, dies zu tun, aber der erste Lauf entfernt 5 Millionen Dateien dauerte Stunden. Läuft jetzt gut. – Matt

Antwort

2

Statt über alle Dateien von looping und Speichern der Dateien, die zum Löschen in einer Liste markiert und dann über jede Datei in der Liste Looping wieder Rohr nur jede Datei auf den nächsten Befehl, wie Sie sie finden.

diesen So ersetzen:

$files = Get-ChildItem -Path $Path -Recurse -Force | Where-Object { !$_.PSIsContainer -and $_.CreationTime -lt $limit } 

If ($files.Count -GT 1) { 
    $files | % {$directory=$_.DirectoryName;(Log-Message "Deleting File $directory\$_");$_ } | Remove-Item -Force 
} 

Damit so etwas wie:

Get-ChildItem -Path $Path -Recurse -Force ` 
| Where-Object { !$_.PSIsContainer -and $_.CreationTime -lt $limit } ` 
| % { 
    $directory=$_.DirectoryName 
    (Log-Message "Deleting File $directory\$_") 
    $_ } ` 
| Remove-Item -Force 
+0

Nicht große Gewinne hier, aber warum mit '$ directory = $ _. DirectoryName' kümmern ... könnte auch einfach tun" "Löschen Datei $ ($ _. FullName) "' – Matt

+0

@Matt Sie müssen das OP fragen, warum. Der Inhalt des "ForEach" -Körpers ist für das Problem nicht wirklich relevant, also habe ich es funktional gleichwertig gelassen. –

+0

Das sieht tatsächlich gut aus. Nicht sicher, warum ich den $ DirectoryName verwendet habe, wahrscheinlich eine lange Nacht :) Das Batching war nur ein Gedanke, um es schneller zu machen, aber ich denke, das könnte funktionieren. – TaylorN

1

Um die Kriterien der das Löschen von Dateien in Chargen von 1000 gerecht zu werden, die folgenden. Das select -first 1000 wird verursachen, es nur 1000 Dateien jedes Mal zu löschen, wenn es die While-Schleife durchläuft.

while($true){ 
    $files = Get-ChildItem -Path $Path -Recurse -Force | Where-Object { !$_.PSIsContainer -and $_.CreationTime -lt $limit } | select -first 1000 
    If ($files.Count -GT 1) { 
     $files | % {$directory=$_.DirectoryName;(Log-Message "Deleting File $directory\$_");$_ } | Remove-Item -Force 
    } else { 
     exit 
    } 
} 

Ich weiß nicht, ob dies schneller sein wird - es hängt davon ab, ob Powershell klug genug ist get-childitem zu stoppen, nachdem es die ersten 1000-Dateien oder nicht findet.

+0

Das habe ich mir gedacht. Ich bin mir nicht sicher, ob das Dosieren die Dinge schneller macht. Meiner Erfahrung nach, aber das ist mehr aus Sicht von SQL/.NET. – TaylorN

+1

Es sieht so aus als würde 'select' die vorherigen Befehle in der Pipeline ab 3.0 [(Dokumentation)] kurzschließen (https://technet.microsoft.com/en-us/library/hh849895.aspx): *" Beginnend In Windows PowerShell 3.0 enthält Select-Object eine Optimierungsfunktion, die verhindert, dass Befehle Objekte erstellen und verarbeiten, die nicht verwendet werden. "* Ein Problem hierbei ist jedoch, dass innerhalb einer Schleife der Befehl" Get-ChildItem "durchlaufen wird alle Dateien, die nicht bei jeder der vorherigen Schleifen gelöscht wurden. –

+0

Anstelle einer groben '$ true' /' exit' Bedingung können Sie einfach die '$ files.Count -GT 1' Logik als Ausgangsbedingung der Schleife verwenden. 'do {#stuff} while ($ files.Count -gt 1)' – Matt

1

Ich muss zugeben, dass ich mich ein wenig irr damit, wie ich robocopy arbeiten wollte. Während es Dateien löschen kann, muss es, wenn es gesagt wird, noch einen Kopiervorgang durchführen. Dieser Vorschlag sollte also am besten auf dem Zielrechner ausgeführt werden und keine UNC-Pfade verwenden. Enttäuschung beiseite, ich denke immer noch, dass dies eine praktikable Lösung ist. Die Hauptsache ist, dass robocopy nur die Dateien auswählt, die wir ohne Nachbearbeitung benötigen.

$sourceDirectory = "D:\temp\New folder" 
$dummyDirectory = "D:\temp\trashbin" 
$loggingFile = "D:\temp\FileCleanupLog-$(Get-Date -f yyyyMMdd_HH_mm_ss).txt" 

# Build the dummy directory. It will be deleted in the end. 
New-Item -Path $dummyDirectory -ItemType Directory | Out-Null 

& robocopy.exe $sourceDirectory /njh /ndl /nc /njs /minage:$days /mov /e /ns /np /l | Set-Content $loggingFile 

# Purge the dummy directory with all the content we don't want 
Remove-Item -Path $dummyDirectory -Force -Confirm:$false -Recurse 

Hier ist, was alle verwendeten Schalter darstellen. Die meisten müssen die Ausgabe für die Protokollierung bereinigen. Das Protokoll sollte nur eine Liste der vollständigen Pfade enthalten, die entfernt wurden. Dies wirkt sich derzeit nicht auf die Verzeichnisstruktur aus. Ein Schalterwechsel wird dies bei Bedarf beheben. Sie werden auch sehen, dass /l nur für die Protokollierung verwendet wird. Sie können diesen Schalter verwenden, um zu prüfen, ob die gewünschten Dateien entfernt werden. Für tatsächliche Produktionstests müssten Sie das entfernen.

 
/minage:N  Specifies the minimum file age (exclude files newer than N days or date). 
/njh    Specifies that there is no job header. 
/njs    Specifies that there is no job summary. 
/l    Specifies that files are to be listed only (and not copied, deleted, or time stamped). 
/mov    Moves files, and deletes them from the source after they are copied. 
/ndl    Specifies that directory names are not to be logged. 
/nc    Specifies that file classes are not to be logged.\ 
/np    Specifies that the progress of the copying operation (the number of files or directories copied so far) will not be displayed. 

Diese Funktion wird auch schneller ausgeführt, wenn die Daten nicht auf dem Bildschirm angezeigt werden. Deshalb setze ich /np ausdrücklich dort ein.