0

尝试实现一个自定义的类似尾巴的功能,在检查了几个示例之后,我来到了下面的代码,它工作得很好(不加载整个文件来读取 X 结束行,适用于网络路径......)

我遇到的问题是我不如何将流指针移动到其当前位置之前的 10 行?

作为一种解决方法,我将指针移动到当前位置之前 1024 个字节,但我不知道这真正涉及到多少行。

$sr=New-Object System.IO.StreamReader($fs)
$lastPosition=$sr.BaseStream.Length # final position of the file
$currentPosition=$lastPosition - 1024

谁能指出我正确的方向吗?

这是完整的代码:

function tail{
    [cmdletBinding()]
    Param(
        [Parameter(Position=0, Mandatory=$true)]
        [ValidateNotNullOrEmpty()]
        [System.String]
        $filename, # path
        [int]$n=10, # number of lines to output
        [switch]$continous, # continue to monitor for changes ?
        [switch]$hilight  # hilight lines containing keywords ?
    )

    # Initialising stuff
    $hilightError=@("erreur","error","fatal","critic")
    $hilightWarning=@("attention","warning","notice")
    $errorColor="red"
    $warningColor="darkyellow"

    if ( (test-Path $filename) -eq $false){
        write-Error "Cant read this file !"
        exit
    }

    function tailFile($ptr){
        # return each line from the pointer position to the end of file
        $sr.BaseStream.Seek($ptr,"Begin")
        $line = $sr.ReadLine() 
        while ( $line -ne $null){
            $e=$w=$false

            if( $hilight){
                $hilightError | %{ $e = $e -or ($line -match $_) } # find error keywords ?
                if( $e) {wh $line -ForegroundColor $errorColor }
                else{ 
                    $hilightWarning | %{ $w = $w -or ($line -match $_ ) } # find warning keywords ?
                    if($w){ wh $line -ForegroundColor $warningColor }
                    else{ wh $line}
                }
            }
            else{ #no hilight
                wh $line
            }
            $line = $sr.ReadLine() 
        }
    }

    # Main 
    $fs=New-Object System.IO.FileStream ($filename,"OpenOrCreate", "Read", "ReadWrite",8,"None") # use ReadWrite sharing permission to not lock the file
    $sr=New-Object System.IO.StreamReader($fs)

    $lastPosition=$sr.BaseStream.Length # final position of the file
    $currentPosition=$lastPosition - 1024 # take some more bytes  (to get the last lines)

    tailfile $currentPosition

    if($continous){
        while(1){
            start-Sleep -s 1
            # have the file changed ?
            if ($sr.BaseStream.Length -eq $lastPosition){
                write-verbose "no change..."
                continue
            }
            else {
                tailfile $lastPosition
                $lastPosition = $sr.BaseStream.Position
            write-Verbose "new position $lastPosition"
            }
        }
    }
    $sr.close()
}
4

2 回答 2

1

您可以在此处查看 PSCXGet-FileTail实现,但它根本不处理 Unicode。而且我确信还有其他可以改进的方法。:-) 欢迎提出建议。

于 2012-12-14T17:18:15.043 回答
0

感谢 Christian 和 Keith 的提示。最后,我决定在流式阅读器中向后退,直到它返回足够的 readline()。我让指定代码页的可能性,并且似乎可以使用 unicode。如果有人感兴趣,这是代码

function tail{
    [cmdletBinding()]
    Param(
        [Parameter(Position=0, Mandatory=$true)]
        [ValidateNotNullOrEmpty()]
        [System.String]
        $filename, # path
        [int]$n=10, # number of lines to output,
        [Alias("cp")]
        $codepage=65001,#utf8
        [Alias("f")]
        [switch]$continous, # continue to monitor for changes ?
        [switch]$hilight  # hilight lines containing keywords ?
    )
   # Initialising stuff
    $hilightError=@("erreur","error","fatal","critic")
    $hilightWarning=@("attention","warning","notice")
    $errorColor="red"
    $warningColor="yellow"
    [System.Text.Encoding]$enc = [System.Text.Encoding]::GetEncoding($codepage)

    function render($render){
        $e=$w=$false
        if( $hilight){
            foreach ($line in $render){
                $hilightError | %{ $e = $e -or ($line -match $_) } # find error keywords ?
                if( $e) {wh $line -ForegroundColor $errorColor }
                else{ 
                    $hilightWarning | %{ $w = $w -or ($line -match $_ ) } # find warning keywords ?
                    if($w){ wh $line -ForegroundColor $warningColor }
                    else{ wh $line}
                }
                $e=$w=$false
            }
        }
        else{ #no hilight
            wh $render
        }
    }


    function TailFileBeforeEnd{
    #try to find $n lines before eof

        $buffer=1024
        $ptr=$lastPosition #start at the end of the file  
        $found=0

        while($ptr -gt 0 -and $found -lt $n){
            $ptr-=$buffer 
            if ($ptr -le 0){$ptr=0}
            $sr.BaseStream.Seek($ptr,"Begin")|out-null #step backward
            $line = $sr.ReadLine()
            $found=0
            $output=@()
            $render=@()

            while ( $line -ne $null){ #read to the end
                $output+=$line
                $found++
                $line=$sr.ReadLine() 
            }
            if($found -ge $n){ #found enough lines
                Write-Verbose "OK found $found / $n"
                foreach($i in ($output.length - $n)..($output.length)){ #take only lines needed
                    $render+=$output[$i]
                }
                continue

            }
            else{ #move backward and retry to find lines
                Write-Verbose "not enough line ($found displayed)"
                $ptr-=$buffer
                if ($ptr -le 0) { #eof without finding suffisant lines
                    $ptr=0
                    Write-host "not enough line ($found displayed)"
                    $render=$output
                }
            }
         }
    render $render
    }


    function tailFile($ptr){
        # return each line from the pointer position to the end of file
        $render=@()
        $sr.BaseStream.Seek($ptr,"Begin")|out-null
        $line = $sr.ReadLine() 
        while ( $line -ne $null){
           $render+=$line
           $line = $sr.ReadLine() 
        }
        render $render
    }

    # Main loop


    # use ReadWrite sharing permission to not lock the file
    $fs=New-Object System.IO.FileStream ($filename,"OpenOrCreate", "Read", "ReadWrite",8,"None") 
    $sr=New-Object System.IO.StreamReader($fs, $enc)
    $lastPosition=$sr.BaseStream.Length 

    tailFileBeforeEnd 

    if($continous){
        while(1){
            start-Sleep -s 2
            # has the file changed ?
            if ($sr.BaseStream.Length -eq $lastPosition){
                write-verbose "no change..."
                continue
            }
            else {
                tailfile $lastPosition
                $lastPosition = $sr.BaseStream.Position
                write-Verbose "new position $lastPosition"
            }
        }
    }
    $sr.close()
}
于 2012-12-16T20:49:55.643 回答