Saya bekerja dengan beberapa file teks multi-gigabyte dan ingin melakukan pemrosesan streaming menggunakan PowerShell. Ini hal sederhana, hanya mengurai setiap baris dan menarik beberapa data, lalu menyimpannya dalam database.
Sayangnya, get-content | %{ whatever($_) }
tampaknya menyimpan seluruh rangkaian garis pada tahap pipa ini dalam memori. Ini juga sangat lambat, membutuhkan waktu yang sangat lama untuk benar-benar membaca semuanya.
Jadi pertanyaan saya ada dua bagian:
- Bagaimana cara membuatnya memproses baris demi baris dan tidak menyimpan semuanya dalam buffer di memori? Saya ingin menghindari penggunaan beberapa gigs RAM untuk tujuan ini.
- Bagaimana cara membuatnya berjalan lebih cepat? PowerShell yang melakukan iterasi pada a
get-content
tampaknya 100x lebih lambat daripada skrip C #.
Saya berharap ada sesuatu yang bodoh yang saya lakukan di sini, seperti melewatkan -LineBufferSize
parameter atau sesuatu ...
powershell
stream
scobi
sumber
sumber
get-content
, set -ReadCount ke 512. Perhatikan bahwa pada titik ini, $ _ di Foreach akan menjadi sebuah array string.Get-Content
ke variabel karena akan memuat seluruh file ke dalam memori. Secara default, dalam pipeline,Get-Content
memproses file satu baris dalam satu waktu. Selama Anda tidak mengumpulkan hasil atau menggunakan cmdlet yang terakumulasi secara internal (seperti Sort-Object dan Group-Object) maka hit memori seharusnya tidak terlalu buruk. Foreach-Object (%) adalah cara yang aman untuk memproses setiap baris, satu per satu.get-content | % -End { }
maka ia mengeluh karena Anda belum menyediakan blok proses. Jadi tidak bisa menggunakan -End secara default, itu harus menggunakan -Process secara default. Dan coba1..5 | % -process { } -end { 'q' }
lihat bahwa blok akhir hanya terjadi sekali, hal biasagc | % { $_ }
tidak akan berfungsi jika scriptblock default menjadi -End ...Jawaban:
Jika Anda benar-benar akan mengerjakan file teks multi-gigabyte, jangan gunakan PowerShell. Bahkan jika Anda menemukan cara untuk membacanya, pemrosesan lebih cepat dari sejumlah besar baris akan menjadi lambat di PowerShell dan Anda tidak dapat menghindari ini. Bahkan loop sederhana pun mahal, katakanlah untuk 10 juta iterasi (cukup nyata dalam kasus Anda) kami memiliki:
# "empty" loop: takes 10 seconds measure-command { for($i=0; $i -lt 10000000; ++$i) {} } # "simple" job, just output: takes 20 seconds measure-command { for($i=0; $i -lt 10000000; ++$i) { $i } } # "more real job": 107 seconds measure-command { for($i=0; $i -lt 10000000; ++$i) { $i.ToString() -match '1' } }
MEMPERBARUI: Jika Anda masih tidak takut, coba gunakan pembaca .NET:
$reader = [System.IO.File]::OpenText("my.log") try { for() { $line = $reader.ReadLine() if ($line -eq $null) { break } # process the line $line } } finally { $reader.Close() }
UPDATE 2
Ada komentar tentang kode yang mungkin lebih baik / lebih pendek. Tidak ada yang salah dengan kode asli
for
dan ini bukan kode semu. Tetapi varian yang lebih pendek (terpendek?) Dari loop pembacaan adalah$reader = [System.IO.File]::OpenText("my.log") while($null -ne ($line = $reader.ReadLine())) { $line }
sumber
do { $line = $reader.ReadLine(); $line } while ($line -neq $null)
for ( $line = $reader.ReadLine(); $line -ne $null; $line = $reader.ReadLine() ) { $line }
while($null -ne ($line = $read.ReadLine())) {$line}
. Tapi sebenarnya topiknya bukan tentang hal-hal seperti itu.System.IO.File.ReadLines()
sempurna untuk skenario ini. Ini mengembalikan semua baris file, tetapi memungkinkan Anda mulai mengulang baris segera yang berarti tidak harus menyimpan seluruh konten dalam memori.Membutuhkan .NET 4.0 atau lebih tinggi.
foreach ($line in [System.IO.File]::ReadLines($filename)) { # do something with $line }
http://msdn.microsoft.com/en-us/library/dd383503.aspx
sumber
Jika Anda ingin menggunakan PowerShell langsung, lihat kode di bawah ini.
$content = Get-Content C:\Users\You\Documents\test.txt foreach ($line in $content) { Write-Host $line }
sumber
Get-Content
sangat lambat pada file besar.