Wenn Sie den Speicherbedarf reduzieren müssen, können Sie die Verwendung von Get-ChildItem
überspringen und stattdessen direkt eine .NET API verwenden. Ich gehe davon aus, dass Sie Powershell v2 verwenden. Wenn dies der Fall ist, befolgen Sie die Schritte here, um .NET 4 zum Laden in Powershell v2 zu aktivieren.
In .NET 4 gibt es einige nette APIs für Enumeration Dateien und Verzeichnisse, im Gegensatz zu ihnen in Arrays zurückgeben.
[IO.Directory]::EnumerateFiles("C:\logs") |%{ <move file $_> }
Durch diese API verwendet, statt [IO.Directory]::GetFiles()
, wird nur ein Dateiname zu einem Zeitpunkt verarbeitet werden, so sollte der Speicherverbrauch relativ klein sein.
bearbeiten
ich auch annahm Sie einen einfachen Pipeline-Ansatz wie Get-ChildItem |ForEach { process }
versucht hatte. Wenn das genug ist, stimme ich zu, dass es der richtige Weg ist.
Aber ich will ein weit verbreitetes Missverständnis klären: In v2, Get-ChildItem
(oder wirklich, die Filesystem-Anbieter) tun nicht wirklich streamen. 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, nicht eine Reihe von reichen Objekten ist. Aber es ist falsch zu behaupten, dass O(1)
Speicher in diesem Muster verwendet wird.
Powershell v3 hingegen ist auf .NET 4 aufgebaut 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.
Danke für die nette und einfache Lösung. Ich hatte immer gedacht, dass Pipelining in Powershell das gesamte Ergebnis vor der Verarbeitung der nächsten Funktion zurückgibt. –
Dies erfordert tatsächlich noch 'O (n)' Speicher, aber wenn es das Problem löst, dann stimme ich zu, es ist die beste Lösung. – latkin