Jak przetwarzać plik w PowerShell linia po linii jako strumień
Pracuję z kilkoma wielogigabajtowymi plikami tekstowymi i chcę je przetworzyć za pomocą PowerShell. To proste, po prostu parsowanie każdej linii i wyciąganie niektórych danych, a następnie przechowywanie ich w bazie danych.
Niestety, get-content | %{ whatever($_) }
wydaje się zachować cały zestaw linii na tym etapie rury w pamięci. Jest również zaskakująco powolny, zajmuje bardzo dużo czasu, aby przeczytać to wszystko.
Więc moje pytanie składa się z dwóch części:
- Jak mogę to przetworzyć streamuj linię po linii i nie zachowuj całej rzeczy buforowanej w pamięci? Chciałbym uniknąć zużywania w tym celu kilku koncertów pamięci RAM.
- Jak sprawić, by biegła szybciej? Iteracja PowerShell ' a nad
get-content
wydaje się być 100x wolniejsza niż skrypt C#.
Mam nadzieję, że robię tu coś głupiego, jak Brak parametru -LineBufferSize
czy coś...
3 answers
Jeśli naprawdę zamierzasz pracować na wielogigabajtowych plikach tekstowych, nie używaj PowerShell. Nawet jeśli znajdziesz sposób, aby go przeczytać szybsze przetwarzanie ogromnej ilości linii będzie powolne w PowerShell i tak i nie można tego uniknąć. Nawet proste pętle są drogie, powiedzmy za 10 milionów iteracji (całkiem realnych w Twoim przypadku) mamy:
# "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' } }
Aktualizacja: jeśli nadal się nie boisz, spróbuj użyć czytnika. 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
Są komentarze o prawdopodobnie lepszym / krótszym kodzie. Nie ma nic złego w oryginalnym kodzie z for
i nie jest to pseudo-kod. Ale krótszy (najkrótszy?) wariantem pętli odczytu jest
$reader = [System.IO.File]::OpenText("my.log")
while($null -ne ($line = $reader.ReadLine())) {
$line
}
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-12-06 15:13:13
System.IO.File.ReadLines()
jest idealny do tego scenariusza. Zwraca wszystkie linie pliku, ale pozwala natychmiast rozpocząć iterację nad liniami, co oznacza, że nie musi przechowywać całej zawartości w pamięci.
Wymaga. NET 4.0 lub wyższej wersji.
foreach ($line in [System.IO.File]::ReadLines($filename)) {
# do something with $line
}
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-12-03 02:13:45
Jeśli chcesz użyć straight PowerShell sprawdź poniższy kod.
$content = Get-Content C:\Users\You\Documents\test.txt
foreach ($line in $content)
{
Write-Host $line
}
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-07-07 21:51:11