PowerShell'de dosya satır satır okuyun


112

PowerShell'de bir dosyayı satır satır okumak istiyorum. Özellikle, dosya boyunca döngü yapmak, her satırı döngüdeki bir değişkende saklamak ve satır üzerinde bazı işlemler yapmak istiyorum.

Bash eşdeğerini biliyorum:

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

PowerShell döngüleri hakkında çok fazla belge yok.


Mathias'ın seçtiği yanıt harika bir çözüm değil. Get-Contenttüm dosyayı bir kerede belleğe yükler, bu da büyük dosyalarda başarısız olur veya donar.
Kolob Canyon

1
@KolobCanyon bu tamamen doğru değil. Get-Content varsayılan olarak her satırı ardışık düzen içinde tek bir nesne olarak yükler. Bir processblok belirtmeyen bir işleve boruluyorsanız ve her satırda boru hattına başka bir nesne tükürüyorsanız, sorun bu işlevdir. Tüm içeriğin belleğe yüklenmesiyle ilgili herhangi bir sorun, hata değildir Get-Content.
The Fish

@TheFish Yinelemeye foreach($line in Get-Content .\file.txt)başlamadan önce tüm dosyayı belleğe yükleyecektir. Bana inanmıyorsanız, 1GB günlük dosyası alın ve deneyin.
Kolob Canyon

2
@KolobCanyon Dediğin bu değil. Get-Content'in hepsini belleğe yüklediğini söylediniz, bu doğru değil. Değişen foreach örneğiniz, evet; foreach boru hattının farkında değildir. Get-Content .\file.txt | ForEach-Object -Process {}ardışık düzen farkındadır ve tüm dosyayı belleğe yüklemeyecektir. Get-Content varsayılan olarak ardışık düzen boyunca her seferinde bir satır geçecektir.
Balık

Yanıtlar:


191

PowerShell döngüleri hakkında çok fazla belge yok.

PowerShell içinde döngüler üzerinde Belgeler bol ve aşağıdaki yardım konularına göz atabilirsiniz: about_For, about_ForEach, about_Do, about_While.

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

Sorununuza başka bir deyimsel PowerShell çözümü, metin dosyasının satırlarını ForEach-Objectcmdlet'e yönlendirmektir :

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

Döngünün içindeki normal ifade eşleşmesi yerine, Where-Objectyalnızca ilgilendiklerinizi filtrelemek için satırları kanalize edebilirsiniz:

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

57

Get-Contentperformansı kötü; dosyayı bir kerede belleğe okumaya çalışır.

C # (.NET) dosya okuyucu her satırı tek tek okur

En İyi Performans

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

Veya biraz daha az performanslı

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

foreachİfadesi muhtemelen biraz daha hızlı olacaktır ForEach-Object(Daha fazla bilgi için aşağıdaki yorumlara bakınız).


5
Muhtemelen kullanırdım [System.IO.File]::ReadLines("C:\path\to\file.txt") | ForEach-Object { ... }. foreachİfadesi olacak bir nesne için koleksiyonun tamamını yüklemek . ForEach-Objectakış yapmak için bir ardışık düzen kullanır. Şimdi foreachifade muhtemelen ForEach-Objectkomuttan biraz daha hızlı olacaktır , ancak bunun nedeni her şeyin belleğe yüklenmesi genellikle daha hızlıdır. Get-Contentyine de korkunç.
Bacon Bits

@BaconBits foreach()takma adıForeach-Object
Kolob Canyon

17
Bu çok yaygın bir yanılgıdır. foreachBir açıklamada, gibidir if, forya da while. ForEach-Objectgibi bir komuttur Get-ChildItem. Orada varsayılan takma da foreachiçin ForEach-Object, ama bir boru hattı varken sadece kullanılır. Uzun açıklamaya bakın Get-Help about_Foreachveya Microsoft'un The Scripting Guys'ın ifade ile komut arasındaki farklar hakkındaki makalesinin tamamına giden önceki yorumumdaki bağlantıya tıklayın.
Bacon Bits

4
@BaconBits blogs.technet.microsoft.com/heyscriptingguy/2014/07/08/… Yeni bir şey öğrendim. Teşekkürler. Aynı olduklarını varsaydım çünkü Get-Alias foreach=> Foreach-Object, ama haklısın, farklılıklar var
Kolob Canyon

2
Bu işe yarayacak, ancak döngünün komut dosyası bloğuna $linegeçmek isteyeceksiniz $_.
Bacon Bits

3

Yüce switchburada iyi çalışır:

'one
two
three' > file

$regex = '^t'

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

Çıktı:

line is two
line is three
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.