PSH V1 Get-Tail

In PSH V2, Get-Content –Tail 10 –Wait will display the last 10 lines of a file, then poll it every second and display any additions to it.  In PSH V1, Get-Content didn’t have a –Tail flag, so if you wanted to do the equivalent of the Unix tail(1), you’d have to let Get-Content scroll through the entire file.  This was my workaround from those days, but it’s still handy to illustrate how to work with FileStreams and StreamReaders.

 

function Get-Tail {
     param (
         [string]$path = $null,
         [int]$last = 10,
         [int]$ms = 250,
         [switch]$follow
     );

     $ErrorActionPreference = $stop;

     try {
         # validate input
         if (!$path) { Write-Warning "-path not specified, required."; return; }
         if (!(Test-Path $path)) { Write-Warning "Unable to find -path $path"; return; }

         # create file stream
         $fileStream = New-Object -ErrorAction SilentlyContinue -TypeName IO.FileStream -ArgumentList ((Get-Item $path).FullName), ([System.IO.FileMode]::Open), ([System.IO.FileAccess]::Read), ([System.IO.FileShare]::ReadWrite);

         # create stream reader for file stream
         $streamReader = New-Object -ErrorAction SilentlyContinue -TypeName System.IO.StreamReader -ArgumentList ($fileStream, [System.Text.Encoding]::ASCII, $true);
         if (!$streamReader) { Write-Warning "Unable to open -path $path"; return; }
     } 
     catch { Write-Warning "Insert error handling here"; }
    
     $baseStream = $streamReader.BaseStream;

     [long]$position = $baseStream.Length - 1;
     $linesCounted = 0;

     # go back -last number of lines;
     while ($position -ge 0) {
         $baseStream.Position = $position;

         if ($baseStream.ReadByte() -eq 10) {
             $linesCounted++;
             if ($position -eq ($baseStream.Length - 1)) { $last++; }
             if ($linesCounted -ge $last) { break; }
         }
         $position--;
     }
    
     # if -last number is greater than length of file, leave at start
     if ($position -eq -1) { $null = $baseStream.Seek(0, [System.IO.SeekOrigin]::Begin); }

     # read from -last number of lines to end of file
     $null0 = [char]0;
     while ($line = $streamReader.ReadLine()) {
         if ($line -notmatch "^$null0$") { $line -replace $null0; }
     }

     # if -follow specified, sleep for -ms milliseconds, then attempt to read again.
     while ($follow) {
         Start-Sleep -Milliseconds $ms;
         while ($line = $streamReader.ReadLine()) {
             if ($line -and ($line -notmatch "^$null0$")) { $line -replace $null0; }
         }
     }
}