Wie wiederhole ich einen Ordner mit einer großen Anzahl von Dateien in PowerShell?

8

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.

    
T.Ho 05.09.2012, 02:26
quelle

3 Antworten

13

Wenn Sie das tun

%Vor%

Sie werden mit Speicherproblemen konfrontiert.

Verwenden Sie PowerShell-Piping, um die Dateien zu verarbeiten:

%Vor%

Der zweite Weg verbraucht weniger Speicher und sollte idealerweise nicht über einen bestimmten Punkt hinaus wachsen.

    
manojlds 05.09.2012, 04:20
quelle
12

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.

    
latkin 05.09.2012 04:24
quelle
0

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!

    
Jean-Pierre de Jong 25.02.2016 14:13
quelle

Tags und Links