PowerShell में लाइन द्वारा फ़ाइल लाइन पढ़ें


100

मैं PowerShell में लाइन द्वारा एक फ़ाइल लाइन पढ़ना चाहता हूं। विशेष रूप से, मैं फ़ाइल के माध्यम से लूप करना चाहता हूं, प्रत्येक पंक्ति को लूप में एक चर में संग्रहीत करता हूं, और लाइन पर कुछ प्रसंस्करण करता हूं।

मैं बैश के बराबर जानता हूं:

while read line do
    if [[ $line =~ $regex ]]; then
          # work here
    fi
done < file.txt

PowerShell लूप पर अधिक प्रलेखन नहीं।


मैथियास से चयनित उत्तर एक महान समाधान नहीं है। Get-Contentएक बार में पूरी फ़ाइल को मेमोरी में लोड करता है, जो बड़ी फ़ाइलों पर विफल या फ्रीज हो जाएगा।
कोलोब कैन्यन

1
@KolobCanyon जो पूरी तरह से असत्य है। डिफ़ॉल्ट रूप से Get-Content पाइपलाइन में प्रत्येक पंक्ति को एक वस्तु के रूप में लोड करता है। यदि आप किसी ऐसे फ़ंक्शन को पाइप कर रहे हैं जो processब्लॉक को निर्दिष्ट नहीं करता है , और पाइप लाइन में प्रति पंक्ति एक अन्य ऑब्जेक्ट को बाहर निकालता है, तो वह फ़ंक्शन समस्या है। पूरी सामग्री को मेमोरी में लोड करने में कोई समस्या नहीं है Get-Content
मछली

@ foreach($line in Get-Content .\file.txt)यह पूरी फ़ाइल को मेमोरी में लोड करेगा, इससे पहले कि यह चलना शुरू कर दे। यदि आप मुझ पर विश्वास नहीं करते हैं, तो 1GB लॉग फ़ाइल प्राप्त करें और इसे आज़माएँ।
कोलोब कैन्यन

2
@KolobCanyon ऐसा नहीं है कि आपने क्या कहा। आपने कहा कि Get-Content इसे सभी मेमोरी में लोड करता है जो सच नहीं है। आपके बदले हुए उदाहरण के लिए, हां; foreach पाइपलाइन की जानकारी नहीं है। Get-Content .\file.txt | ForEach-Object -Process {}पाइप लाइन से अवगत है, और पूरी फ़ाइल को मेमोरी में लोड नहीं करेगा। डिफ़ॉल्ट रूप से गेट-कंटेंट पाइप लाइन के माध्यम से एक बार में एक लाइन से गुजरेगा।
मछली

जवाबों:


176

PowerShell लूप पर अधिक प्रलेखन नहीं।

PowerShell में छोरों पर प्रलेखन भरपूर मात्रा में है, और आप नीचे दी गई सहायता विषयों की जांच करना चाह सकता है: about_For, about_ForEach, about_Do, about_While

foreach($line in Get-Content .\file.txt) {
    if($line -match $regex){
        # Work here
    }
}

आपकी समस्या का एक और मुहावरेदार PowerShell समाधान पाठ फ़ाइल की पंक्तियों को ForEach-Objectcmdlet में पाइप करना है :

Get-Content .\file.txt | ForEach-Object {
    if($_ -match $regex){
        # Work here
    }
}

लूप के अंदर रेगेक्स मिलान के बजाय, आप Where-Objectउन पंक्तियों को फ़िल्टर कर सकते हैं, जिन्हें आप चाहते हैं:

Get-Content .\file.txt | Where-Object {$_ -match $regex} | ForEach-Object {
    # Work here
}

लिंक टूटे नहीं हैं, लेकिन वे अब पुनर्निर्देशित हैं docs.microsoft.com
पीटर मोर्टेंसन

@KolobCanyon जिसे ओपी पर एक मुद्दे के रूप में कभी उल्लेख नहीं किया गया था।
मछली

53

Get-Contentखराब प्रदर्शन है; यह फ़ाइल को एक साथ मेमोरी में पढ़ने की कोशिश करता है।

C # (.NET) फ़ाइल रीडर प्रत्येक पंक्ति को एक-एक करके पढ़ता है

सर्वश्रेष्ठ प्रदर्शन

foreach($line in [System.IO.File]::ReadLines("C:\path\to\file.txt"))
{
       $line
}

या थोड़ा कम प्रदर्शन करने वाला

[System.IO.File]::ReadLines("C:\path\to\file.txt") | ForEach-Object {
       $_
}

foreachबयान संभावना थोड़ा तेज हो जाएगा की तुलना में ForEach-Object(अधिक जानकारी के लिए नीचे टिप्पणी देखें)।


5
मैं शायद उपयोग करूंगा [System.IO.File]::ReadLines("C:\path\to\file.txt") | ForEach-Object { ... }foreachबयान होगा एक वस्तु को पूरा संग्रह लोडForEach-Objectके साथ स्ट्रीम करने के लिए एक पाइपलाइन का उपयोग करता है। अब foreachबयान संभवतः ForEach-Objectकमांड की तुलना में थोड़ा तेज होगा , लेकिन ऐसा इसलिए है क्योंकि आमतौर पर मेमोरी को पूरी तरह से लोड करना तेज है। Get-Contentहालांकि अभी भी भयानक है।
बेकन बिट्स

@ बैकोनिट्स foreach()का एक उपनाम हैForeach-Object
कैनियन

16
यह एक बहुत ही आम गलत धारणा है। foreachहै एक बयान, जैसे if, for, या whileForEach-Objectएक कमांड है, जैसे Get-ChildItem। के foreachलिए एक डिफ़ॉल्ट उपनाम भी है ForEach-Object, लेकिन इसका उपयोग केवल तब होता है जब पाइप लाइन होती है। लंबे विवरण को देखें Get-Help about_Foreach, या मेरी पिछली टिप्पणी में लिंक पर क्लिक करें, जो Microsoft के द स्क्रिप्टिंग दोस्तों द्वारा बयान और आदेश के बीच अंतर के बारे में पूरे लेख पर जाता है।
बेकन बिट्स

4
@BaconBits blogs.technet.microsoft.com/heyscriptingguy/2014/07/08/… कुछ नया सीखा। धन्यवाद। मुझे लगता है कि वे एक ही थे क्योंकि Get-Alias foreach=> Foreach-Object, लेकिन आप सही हैं, मतभेद हैं
Kolob Canyon

2
यही कारण है कि काम करेंगे, लेकिन आप बदलना चाहते हैं जाएगा $lineकरने के लिए $_पाश की स्क्रिप्ट ब्लॉक में।
बेकन बिट्स

1

सर्वशक्तिमान स्विच यहाँ अच्छी तरह से काम करता है:

'one
two
three' > file

$regex = '^t'

switch -regex -file file { 
  $regex { "line is $_" } 
}

आउटपुट:

line is two
line is three
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.