无需streamreader,即可在大型.log中快速搜索特定字符串

时间:2019-04-16 19:12:20

标签: powershell powershell-v2.0 powershell-remoting

问题:我需要搜索另一个进程当前正在使用的大型日志文件。我无法停止此其他过程,也无法锁定.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 :(得分:0)

也许这对您有用。它会尝试尽可能快地读取文件的各行,但与您的第二种方法有所不同(与[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{...}

希望有帮助