Поиск большого .log для конкретной строки быстро без потокового чтения - PullRequest
0 голосов
/ 16 апреля 2019

Проблема: мне нужно найти большой файл журнала, который в данный момент используется другим процессом.Я не могу остановить этот другой процесс или установить блокировку файла .log.Мне нужно быстро найти этот файл, и я не могу прочитать все это в памяти.Я понял, что StreamReader () - самый быстрый, но я не могу понять, как избежать попыток захватить блокировку файла.

$p = "Seachterm:Search"
$files = "\\remoteserver\c\temp\tryingtofigurethisout.log"

$SearchResult= Get-Content -Path $files | Where-Object { $_ -eq $p }  

Ниже не работает, потому что я не могуполучить блокировку файла.

$reader = New-Object System.IO.StreamReader($files)

$lines = @()

if ($reader -ne $null) {
    while (!$reader.EndOfStream) {
        $line = $reader.ReadLine()
        if ($line.Contains($p)) {
            $lines += $line
        }
    }
}

$lines | Select-Object -Last 1

Это занимает слишком много времени:

get-content $files -ReadCount 500 |
 foreach { $_ -match $p }

Я был бы очень признателен за любые указания о том, как можно быстро и эффективно (с точки зрения памяти) искать большой файл журнала.

1 Ответ

0 голосов
/ 16 апреля 2019

Возможно, это будет работать для вас.Он пытается прочитать строки файла как можно быстрее, но с отличием от вашего второго подхода (который примерно равен тому, что сделал бы [System.IO.File]::ReadAllLines()).

Чтобы собрать строки, яиспользовать объект List, который будет работать быстрее, чем добавление в массив с помощью +=

$p    = "Seachterm:Search"
$path = "\\remoteserver\c$\temp\tryingtofigurethisout.log"

if (!(Test-Path -Path $path -PathType Leaf)) {
    Write-Warning "File '$path' does not exist"
}
else {
    try {
        $fileStream   = [System.IO.FileStream]::new($path, [System.IO.FileMode]::Open, [System.IO.FileAccess]::Read, [System.IO.FileShare]::ReadWrite)
        $streamReader = [System.IO.StreamReader]::new($fileStream)

        # or use this syntax:
        # $fileMode     = [System.IO.FileMode]::Open
        # $fileAccess   = [System.IO.FileAccess]::Read
        # $fileShare    = [System.IO.FileShare]::ReadWrite
        # $fileStream   = New-Object -TypeName System.IO.FileStream $path, $fileMode, $fileAccess, $fileShare
        # $streamReader = New-Object -TypeName System.IO.StreamReader -ArgumentList $fileStream

        # use a List object of type String or an ArrayList to collect the strings quickly
        $lines = New-Object System.Collections.Generic.List[string]
        # read the lines as fast as you can and add them to the list
        while (!$streamReader.EndOfStream) {
            $lines.Add($streamReader.ReadLine())
        }
        # close and dispose the obects used
        $streamReader.Close()
        $fileStream.Dispose()

        # do the 'Contains($p)' after reading the file to not slow that part down
        $lines.ToArray() | Where-Object { $_.Contains($p) } | Select-Object -Last 1
    }
    catch [System.IO.IOException] {}
}

. В основном, он выполняет то же, что и ваш второй код, но с той разницей, что при использовании только StreamReader файлаоткрывается с помощью [System.IO.FileShare]::Read, тогда как этот код открывает файл с [System.IO.FileShare]::ReadWrite

Обратите внимание, что при этом могут возникать исключения, поскольку другое приложение имеет права на запись в файл, следовательно, try{...} catch{...}

Надеюсь, это поможет

...