Ich versuche ein Skript zu schreiben, das 1,6 Millionen Dateien in einem Ordner durchläuft und sie basierend auf dem Dateinamen in den richtigen Ordner verschiebt.
Der Grund dafür ist, dass NTFS eine große Anzahl von Dateien in einem einzelnen Ordner nicht verarbeiten kann, ohne die Leistung zu beeinträchtigen.
Der Skriptaufruf "Get-ChildItem" ruft alle Elemente in diesem Ordner ab, und wie Sie vielleicht erwarten, verbraucht dies viel Arbeitsspeicher (etwa 3,8 GB).
Ich bin neugierig, ob es andere Möglichkeiten gibt, alle Dateien in einem Verzeichnis zu durchlaufen, ohne so viel Speicher zu verbrauchen.
Wenn Sie den Speicherbedarf reduzieren müssen, können Sie Get-ChildItem
überspringen und stattdessen direkt eine .NET API verwenden. Ich gehe davon aus, dass Sie auf Powershell v2 sind, wenn dies der Fall ist, folgen Sie zuerst den Schritten hier , damit .NET 4 in Powershell v2 geladen werden kann.
In .NET 4 gibt es einige nette APIs für Enumeration Dateien und Verzeichnisse, anstatt sie in Arrays zurückzugeben.
%Vor% Bei Verwendung dieser API wird anstelle von [IO.Directory]::GetFiles()
nur ein Dateiname gleichzeitig verarbeitet, so dass der Speicherverbrauch relativ gering sein sollte.
Bearbeiten
Ich nahm auch an, dass Sie einen einfachen Pipeline-Ansatz wie Get-ChildItem |ForEach { process }
versucht haben. Wenn das genug ist, stimme ich zu, es ist der Weg zu gehen.
Aber ich möchte ein häufiges Missverständnis aufklären: In v2 läuft Get-ChildItem
(oder wirklich, der FileSystem-Provider) wirklich nicht . Die Implementierung verwendet die APIs Directory.GetDirectories
und Directory.GetFiles
, die in Ihrem Fall ein Array mit 1,6M-Elementen generieren, bevor eine Verarbeitung stattfinden kann. Sobald dies geschehen ist, ja, der Rest der Pipeline streamt. Und ja, diese anfängliche Low-Level-Stück hat relativ minimale Auswirkungen, da es einfach ein String-Array ist, nicht ein Array von reichen FileInfo
Objekte. Aber es ist falsch zu behaupten, dass O(1)
Speicher in diesem Muster verwendet wird.
Powershell v3 dagegen basiert auf .NET 4 und nutzt daher die oben erwähnten Streaming-APIs ( Directory.EnumerateDirectories
und Directory.EnumerateFiles
). Das ist eine nette Abwechslung und hilft in Szenarien wie deiner.
So habe ich es implementiert, ohne .Net 4.0 zu verwenden. Nur Powershell 2.0 und altmodischer DIR-Befehl:
Es sind nur zwei Zeilen (einfachen) Code:
%Vor%My Powershell Proces verwendet nur 15 MB. Keine Änderungen auf dem alten Windows 2008 Server!
Prost!
Tags und Links powershell