Nếu bạn cần giảm dung lượng bộ nhớ, bạn có thể bỏ qua sử dụng Get-ChildItem
và thay vào đó sử dụng .NET API trực tiếp. Tôi giả sử bạn đang ở trên Powershell v2, nếu như vậy đầu tiên hãy làm theo các bước here để bật .NET 4 để tải trong Powershell v2.
Trong .NET 4, có một số API tốt đẹp để liệt kê tệp và thư mục, thay vì trả lại chúng trong mảng.
[IO.Directory]::EnumerateFiles("C:\logs") |%{ <move file $_> }
Bằng cách sử dụng API này, thay vì [IO.Directory]::GetFiles()
, chỉ có một tên tập tin sẽ được xử lý tại một thời điểm, vì vậy mức tiêu thụ bộ nhớ nên tương đối nhỏ.
Sửa
Tôi cũng giả sử bạn đã cố gắng một cách tiếp cận đơn giản như pipelined Get-ChildItem |ForEach { process }
. Nếu điều này là đủ, tôi đồng ý đó là con đường để đi.
Nhưng tôi muốn làm sáng tỏ quan niệm sai lầm phổ biến: Trong v2, Get-ChildItem
(hoặc thực sự, nhà cung cấp Hệ thống tệp) thực hiện không phải là luồng thực sự. Việc triển khai sử dụng các API Directory.GetDirectories
và Directory.GetFiles
, trong trường hợp của bạn sẽ tạo ra một mảng yếu tố 1,6M trước khi bất kỳ quá trình xử lý nào có thể xảy ra. Khi điều này được thực hiện, thì có, phần còn lại của kênh đang phát trực tuyến. Và có, đoạn ban đầu thấp này có tác động tương đối tối thiểu, vì nó chỉ đơn giản là một mảng chuỗi, không phải là một mảng các đối tượng phong phú FileInfo
. Nhưng không đúng khi tuyên bố rằng bộ nhớ O(1)
được sử dụng trong mẫu này.
Ngược lại, Powershell v3 được xây dựng trên .NET 4 và do đó tận dụng các API phát trực tiếp mà tôi đề cập ở trên (Directory.EnumerateDirectories
và Directory.EnumerateFiles
). Đây là một thay đổi tốt đẹp, và giúp trong các tình huống giống như của bạn.
Cảm ơn vì giải pháp đơn giản và tốt đẹp. Tôi đã luôn luôn nghĩ pipelining trong PowerShell trả lại toàn bộ kết quả trước khi xử lý chức năng tiếp theo. –
Điều này thực sự vẫn đòi hỏi bộ nhớ 'O (n)', nhưng nếu nó giải quyết được vấn đề thì tôi đồng ý đó là giải pháp tốt nhất. – latkin