Đọc từng dòng tệp trong PowerShell


100

Tôi muốn đọc từng dòng một tệp trong PowerShell. Cụ thể, tôi muốn lặp qua tệp, lưu trữ từng dòng trong một biến trong vòng lặp và thực hiện một số xử lý trên dòng.

Tôi biết tương đương Bash:

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

Không có nhiều tài liệu về vòng lặp PowerShell.


Câu trả lời được chọn từ Mathias không phải là một giải pháp tuyệt vời. Get-Contenttải toàn bộ tệp vào bộ nhớ cùng một lúc, việc này sẽ bị lỗi hoặc bị đóng băng trên các tệp lớn.
Kolob Canyon

@KolobCanyon hoàn toàn không đúng sự thật. Theo mặc định Get-Content tải mỗi dòng dưới dạng một đối tượng trong đường dẫn. Nếu bạn đang đường ống đến một hàm không chỉ định processkhối và phun ra một đối tượng khác trên mỗi dòng vào đường ống, thì hàm đó là vấn đề. Mọi vấn đề với việc tải toàn bộ nội dung vào bộ nhớ không phải do lỗi của Get-Content.
The Fish

@TheFish foreach($line in Get-Content .\file.txt)Nó sẽ tải toàn bộ tệp vào bộ nhớ trước khi bắt đầu lặp lại. Nếu bạn không tin tôi, hãy lấy một tệp nhật ký 1GB và thử nó.
Kolob Canyon

1
@KolobCanyon Đó không phải là những gì bạn nói. Bạn nói rằng Get-Content tải tất cả vào bộ nhớ là không đúng. Ví dụ đã thay đổi của bạn về foreach sẽ, có; foreach không nhận biết đường ống. Get-Content .\file.txt | ForEach-Object -Process {}là đường ống nhận biết và sẽ không tải toàn bộ tệp vào bộ nhớ. Theo mặc định, Get-Content sẽ chuyển từng dòng một trong đường dẫn.
The Fish

Câu trả lời:


176

Không có nhiều tài liệu về vòng lặp PowerShell.

Tài liệu về các vòng lặp trong PowerShell là dồi dào, và bạn có thể muốn kiểm tra các chủ đề trợ giúp sau: about_For, about_ForEach, about_Do, about_While.

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

Một giải pháp PowerShell thành ngữ khác cho vấn đề của bạn là chuyển các dòng của tệp văn bản đến ForEach-Objectlệnh ghép ngắn :

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

Thay vì so khớp regex bên trong vòng lặp, bạn có thể chuyển các dòng qua Where-Objectđể chỉ lọc những người bạn quan tâm:

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

Các liên kết không bị hỏng, nhưng bây giờ chúng chuyển hướng đến docs.microsoft.com.
Peter Mortensen

@KolobCanyon chưa bao giờ được đề cập đến như một vấn đề trên OP.
The Fish

52

Get-Contentcó hiệu suất không tốt; nó cố gắng đọc toàn bộ tệp vào bộ nhớ cùng một lúc.

Trình đọc tệp C # (.NET) đọc từng dòng một

Biểu diễn tốt nhất

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

Hoặc kém hiệu suất hơn một chút

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

Các foreachtuyên bố có thể sẽ nhanh hơn một chút so với ForEach-Object(xem ý kiến dưới đây để biết thêm thông tin).


5
Tôi có thể sẽ sử dụng [System.IO.File]::ReadLines("C:\path\to\file.txt") | ForEach-Object { ... }. Câu foreachlệnh sẽ tải toàn bộ tập hợp vào một đối tượng . ForEach-Objectsử dụng một đường ống để phát trực tiếp. Bây giờ foreachcâu lệnh có thể sẽ nhanh hơn một chút so với câu ForEach-Objectlệnh, nhưng đó là vì tải toàn bộ nội dung vào bộ nhớ thường nhanh hơn. Get-Contentvẫn còn khủng khiếp, tuy nhiên.
Bacon Bits

@BaconBits foreach()là bí danh củaForeach-Object
Kolob Canyon,

15
Đó là một quan niệm sai lầm rất phổ biến. foreachlà một tuyên bố, như if, forhoặc while. ForEach-Objectlà một lệnh, giống như Get-ChildItem. Ngoài ra còn có một bí danh mặc định là foreachfor ForEach-Object, nhưng nó chỉ được sử dụng khi có một đường dẫn. Xem phần giải thích dài trong Get-Help about_Foreachhoặc nhấp vào liên kết trong nhận xét trước đây của tôi dẫn đến toàn bộ bài viết của The Scripting Guys của Microsoft về sự khác biệt giữa câu lệnh và lệnh.
Bacon Bits

3
@BaconBits blog.technet.microsoft.com/heyscriptingguy/2014/07/08/… Đã học được điều gì đó mới. Cảm ơn. Tôi cho rằng chúng giống nhau bởi vì Get-Alias foreach=> Foreach-Object, nhưng bạn nói đúng, có sự khác biệt
Kolob Canyon

2
Điều đó sẽ hoạt động, nhưng bạn sẽ muốn thay đổi $linethành $_trong khối tập lệnh của vòng lặp.
Bacon Bits

1

Công tắc toàn năng hoạt động tốt ở đây:

'one
two
three' > file

$regex = '^t'

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

Đầu ra:

line is two
line is three
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.