อ่านไฟล์ทีละบรรทัดใน PowerShell


112

ฉันต้องการอ่านไฟล์ทีละบรรทัดใน PowerShell โดยเฉพาะฉันต้องการวนซ้ำไฟล์เก็บแต่ละบรรทัดในตัวแปรในลูปและดำเนินการประมวลผลบางอย่างในบรรทัด

ฉันรู้ว่าเทียบเท่า Bash:

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

เอกสารประกอบเกี่ยวกับลูป PowerShell ไม่มากนัก


คำตอบที่เลือกจาก Mathias ไม่ใช่ทางออกที่ดี Get-Contentโหลดไฟล์ทั้งหมดลงในหน่วยความจำพร้อมกันซึ่งจะล้มเหลวหรือค้างในไฟล์ขนาดใหญ่
Kolob Canyon

1
@KolobCanyon ที่ไม่เป็นความจริงโดยสิ้นเชิง โดยค่าเริ่มต้น Get-Content จะโหลดแต่ละบรรทัดเป็นวัตถุเดียวในไปป์ไลน์ หากคุณกำลังไพพ์ไปยังฟังก์ชันที่ไม่ได้ระบุprocessบล็อกและพ่นอ็อบเจ็กต์อื่นต่อบรรทัดลงในไปป์ไลน์แสดงว่าฟังก์ชันนั้นเป็นปัญหา ปัญหาใด ๆ ในการโหลดเนื้อหาทั้งหมดลงในหน่วยความจำไม่ใช่ความผิดของGet-Content.
The Fish

@TheFish foreach($line in Get-Content .\file.txt)มันจะโหลดไฟล์ทั้งหมดลงในหน่วยความจำก่อนที่จะเริ่มทำซ้ำ ถ้าคุณไม่เชื่อฉันไปรับไฟล์บันทึก 1GB แล้วลองดู
Kolob Canyon

2
@KolobCanyon นั่นไม่ใช่สิ่งที่คุณพูด คุณบอกว่า Get-Content โหลดทั้งหมดลงในหน่วยความจำซึ่งไม่เป็นความจริง ตัวอย่าง foreach ที่เปลี่ยนไปของคุณจะใช่ foreach ไม่ทราบไปป์ไลน์ Get-Content .\file.txt | ForEach-Object -Process {}ไปป์ไลน์รับรู้และจะไม่โหลดไฟล์ทั้งหมดลงในหน่วยความจำ โดยค่าเริ่มต้น Get-Content จะส่งทีละบรรทัดผ่านไปป์ไลน์
The Fish

คำตอบ:


191

เอกสารประกอบเกี่ยวกับลูป 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
    }
}

แทนที่จะจับคู่ regex ภายในลูปคุณสามารถไปป์ไลน์Where-Objectเพื่อกรองเฉพาะคนที่คุณสนใจ:

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

57

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อย่างไรก็ตามยังคงแย่มาก
Bacon Bits

@BaconBits foreach()เป็นนามแฝงของForeach-Object
Kolob Canyon

17
นั่นเป็นความเข้าใจผิดที่พบบ่อยมาก foreachเป็นคำสั่งที่ชอบif, หรือ for เป็นคำสั่งเช่น. นอกจากนี้ยังมีนามแฝงเริ่มต้นสำหรับแต่จะใช้เฉพาะเมื่อมีไปป์ไลน์ ดูคำอธิบายแบบยาวในหรือคลิกลิงก์ในความคิดเห็นก่อนหน้าของฉันซึ่งไปยังบทความทั้งหมดโดย The Scripting Guys ของ Microsoft เกี่ยวกับความแตกต่างระหว่างคำสั่งและคำสั่ง whileForEach-ObjectGet-ChildItemforeachForEach-ObjectGet-Help about_Foreach
Bacon Bits

4
@BaconBits blogs.technet.microsoft.com/heyscriptingguy/2014/07/08/…ได้ เรียนรู้อะไรใหม่ ๆ ขอบคุณ. ฉันคิดว่าพวกเขาเหมือนกันเพราะGet-Alias foreach=> Foreach-Objectแต่คุณพูดถูกมีความแตกต่าง
Kolob Canyon

2
จะได้ผล แต่คุณจะต้องเปลี่ยน$lineเป็น$_ในบล็อกสคริปต์ของลูป
Bacon Bits

3

ผู้ทรงอำนาจswitchทำงานได้ดีที่นี่:

'one
two
three' > file

$regex = '^t'

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

เอาท์พุต:

line is two
line is three
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.