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

0

ヒントをくれた Christian と Keith に感謝します。最後に、十分な数の readline() が返されるまで、streamreader を元に戻すことにしました。コードページを指定できるようにしましたが、ユニコードで問題ないようです。誰かが興味を持っている場合のコードは次のとおりです

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 に答える