2012-09-05 5 views
8

मैं एक स्क्रिप्ट जो किसी फ़ोल्डर में 16 लाख फ़ाइलों के माध्यम से जाने के लिए और उन्हें फ़ाइल नाम के आधार पर सही फ़ोल्डर में ले जाएगा लिखने की कोशिश कर रहा हूँ।PowerShell में बड़ी संख्या में फ़ाइलों वाले फ़ोल्डर पर पुनरावृत्ति कैसे करें?

कारण यह है कि NTFS एक ही फ़ोल्डर में फ़ाइलें की एक बड़ी संख्या के प्रदर्शन में नीचा बिना नहीं संभाल सकता है।

स्क्रिप्ट कॉल "गेट-चाइल्ड इटिम" उस फ़ोल्डर के भीतर सभी आइटम प्राप्त करने के लिए कॉल करें, और जैसा कि आप उम्मीद कर सकते हैं, यह बहुत मेमोरी (लगभग 3.8   जीबी) का उपभोग करता है।

मैं अगर वहाँ इतना स्मृति का उपयोग किए बिना एक निर्देशिका में सभी फ़ाइलों के माध्यम से पुनरावृति के लिए किसी भी अन्य तरीके हैं उत्सुक हूँ।

उत्तर

13

यदि आप करते हैं

$files = Get-ChildItem $dirWithMillionsOfFiles 
#Now, process with $files 

आप स्मृति मुद्दों का सामना करना पड़ेगा।

उपयोग PowerShell पाइपिंग फ़ाइलों को प्रोसेस करने:

Get-ChildItem $dirWithMillionsOfFiles | %{ 
    #process here 
} 

दूसरा तरीका कम स्मृति की खपत होगी और आदर्श एक निश्चित बिंदु से आगे नहीं करना चाहिए।

+0

अच्छा और सरल समाधान के लिए धन्यवाद। मैंने हमेशा सोचा था कि शक्तियों में पाइपलाइनिंग अगले कार्य को संसाधित करने से पहले पूरे परिणाम को वापस कर देगी। –

+2

यह वास्तव में अभी भी 'ओ (एन)' स्मृति की आवश्यकता है, लेकिन यदि यह समस्या हल करता है तो मैं मानता हूं कि यह सबसे अच्छा समाधान है। – latkin

12

आप स्मृति पदचिह्न को कम करने की जरूरत है, तो आप Get-ChildItem का उपयोग कर दें और इसके बजाय सीधे एक .NET एपीआई का उपयोग करें। मैं यह सोचते करती हूं कि आप PowerShell V2 पर कर रहे हैं, यदि ऐसा है तो पहले PowerShell V2 में लोड करने के लिए .NET 4 सक्षम करने के चरण here का पालन करें।

.NET 4 में फाइलों और निर्देशिकाओं के लिए कुछ अच्छे एपीआई हैं, जो उन्हें सरणी में लौटने के विपरीत हैं।

[IO.Directory]::EnumerateFiles("C:\logs") |%{ <move file $_> } 

इस एपीआई [IO.Directory]::GetFiles() का उपयोग कर के बजाय करके, केवल एक ही फ़ाइल नाम एक समय में कार्रवाई की जाएगी, तो स्मृति की खपत अपेक्षाकृत छोटे होना चाहिए।

संपादित

मैं भी यह सोचते हैं आप था Get-ChildItem |ForEach { process } की तरह एक साधारण pipelined दृष्टिकोण की कोशिश की थी। यदि यह पर्याप्त है, तो मैं सहमत हूं कि यह जाने का तरीका है।

लेकिन मैं एक आम गलत धारणा स्पष्ट करने के लिए करना चाहते हैं: वी 2 में, Get-ChildItem (या वास्तव में, FileSystem प्रदाता) सही मायने में स्ट्रीम नहीं करता है। कार्यान्वयन एपीआई Directory.GetDirectories और Directory.GetFiles है, जो अपने मामले में एक 1.6M-तत्व वाली सरणी से पहले किसी भी प्रसंस्करण हो सकता है उत्पन्न होगा उपयोग करता है। एक बार यह हो जाने के बाद, हाँ, पाइपलाइन का शेष स्ट्रीमिंग हो रहा है। और हां, इस शुरुआती निम्न-स्तरीय टुकड़े के अपेक्षाकृत कम प्रभाव पड़ता है, क्योंकि यह केवल एक स्ट्रिंग सरणी है, अमीर FileInfo ऑब्जेक्ट्स की एक सरणी नहीं है। लेकिन यह दावा करना गलत है कि इस पैटर्न में O(1) मेमोरी का उपयोग किया जाता है।

इसके विपरीत, पावरहेल v3, .NET 4 पर बनाया गया है, और इस प्रकार ऊपर उल्लिखित स्ट्रीमिंग एपीआई का लाभ उठाता है (Directory.EnumerateDirectories और Directory.EnumerateFiles)। यह एक अच्छा बदलाव है, और परिदृश्य में आपकी तरह ही मदद करता है।

+0

मुझे लगता है कि गेट-चाइल्ड इटैम के साथ पाइपलाइन का उपयोग करना जैसे कि मैनोजल्ड ने सुझाव दिया था कि एक ही चीज़ प्राप्त होगी, लेकिन मुझे दिखाए जाने के लिए धन्यवाद। शक्तियों के साथ नेट! :)। –

+0

हाँ, get-childitem | foreach-objetc {...} एक समय के रूप में केवल एक पास आइटम को संसाधित करेगा। – x0n

+1

मेरा संपादन देखें। 'get-childitem | foreach {...} 'केवल छद्म स्ट्रीमिंग है, यह तकनीकी रूप से अभी भी' ओ (एन) 'स्मृति की आवश्यकता है। – latkin

0

इस प्रकार मैंने इसे .NET 4.0 का उपयोग किए बिना कार्यान्वित किया। केवल पावरहेल 2।0 और पुराने ढंग का DIR-आदेश:

यह (आसान) कोड के सिर्फ 2 लाइनों है:

cd <source_path> 
cmd /c "dir /B"| % { move-item $($_) -destination "<dest_folder>" } 

मेरे Powershell प्रोसेसर केवल 15MB उपयोग करता है। पुराने विंडोज 2008 सर्वर पर कोई बदलाव नहीं!

चीयर्स!

संबंधित मुद्दे