A Software Architect Living in a Networking World

Joe Pruitt

Subscribe to Joe Pruitt: eMailAlertsEmail Alerts
Get Joe Pruitt: homepageHomepage mobileMobile rssRSS facebookFacebook twitterTwitter linkedinLinkedIn


Blog Feed Post

Unix To PowerShell - Tail

PowerShell_unix PowerShell is definitely gaining momentum in the windows scripting world but I still hear folks wanting to rely on Unix based tools to get their job done.  In this series of posts I’m going to look at converting some of the more popular Unix based tools to PowerShell.

tail

The Unix “tail” command that is used to display the last 10 lines of each FILE to standard output.  With more than one file, precede each with a header giving the file name.  There is also a mode where it prints out the last “n” bytes in a file.  And for those that want to monitor changes to a file, there is the “follow” option where tail will monitor the file and print out any additions as they are made.

I’ve implemented these three options with the follow option only working on line mode.  The script could be made to work on byte mode as well, but I’ll leave that to the reader to implement if you really want it.

The unix parameters map to the following in my PowerShell script:

Unix PowerShell Description
-c -num_bytes Output the last N bytes.
-n -num_lines Output the last N lines (default 10).
-f -follow Output appended data as the file grows.
-s -sleep With “-f”, sleep for N seconds between iterations (default 1).
-q -quiet Never output headers giving file names.

The code will loop through the specified files.  For “num line” mode, it will get the contents of the file into a string array and print out the last “n” lines with the default being 10.  If the "-follow” switch was given, it will sit in a loop waiting the specified number of seconds before rechecking the file and if any modifications have been made it will print them to the console.  This is repeated indefinitely until the script is broken.

For byte mode, the content will be loaded into a string and the last “n” characters (up to the size of the file) will be displayed to the console.

   1: #----------------------------------------------------------------
   2: # Tail.ps1
   3: #----------------------------------------------------------------
   4: param
   5: (
   6:     [string]$filespec = $null,
   7:     [int]$num_bytes = -1,
   8:     [int]$num_lines = -1,
   9:     [bool]$follow = $false,
  10:     [int]$sleep = 1,
  11:     [bool]$quiet = $false
  12: );
  13:  
  14: function Do-Tail()
  15: {
  16:     param
  17:     (
  18:         [string]$filespec = $null,
  19:         [int]$num_bytes = -1,
  20:         [int]$num_lines = -1,
  21:         [bool]$follow = $false,
  22:         [int]$sleep = 1,
  23:         [bool]$quiet = $false
  24:     );
  25:     
  26:     # if no bytes or lines specified, default to 10 lines
  27:     if ( (-1 -eq $num_bytes) -and (-1 -eq $num_lines) ) { $num_lines = 10; }
  28:     
  29:     $files = @(Get-ChildItem $filespec);
  30:     foreach ($file in $files)
  31:     {
  32:         # Optionally output file names when multiple files given
  33:         if ( ($files.Length -gt 1) -and !$quiet ) { Write-Host "==> $($file.Name) <=="; }
  34:     
  35:         if ( -1 -ne $num_lines )
  36:         {
  37:             $prev_len = 0;
  38:             while ($true)
  39:             {
  40:                 # For line number option, get content as an array of lines
  41:                 # and print out the last "n" of them.
  42:                 $lines = Get-Content $file;
  43:                 
  44:                 if ( $prev_len -ne 0 ) { $num_lines = $lines.Length - $prev_len; }
  45:                 
  46:                 $start_line = $lines.Length - $num_lines;
  47:                 
  48:                 # Ensure that we don't go past the beginning of the input
  49:                 if ( $start_line -le 0 ) { $start_line = 0; }
  50:                 
  51:                 for ($i = $start_line; $i -lt $lines.Length; $i++)
  52:                 {
  53:                     $lines[$i];
  54:                 }
  55:                 $prev_len = $lines.Length;
  56:                 
  57:                 # If we are following the file, sleep the desired interval
  58:                 # else break out of the loop and continue with the next file.
  59:                 if ( $follow )
  60:                 {
  61:                     Start-Sleep $sleep;
  62:                 }
  63:                 else
  64:                 {
  65:                     break;
  66:                 }
  67:             }
  68:         }
  69:         elseif ( -1 -ne $num_bytes )
  70:         {
  71:             # for num bytes option, get the content as a single string 
  72:             # and substring the last "n" bytes.
  73:             [string]$content = Get-Content $file -delim [char]0;
  74:             
  75:             if ( ($content.Length - $num_bytes) -lt 0 ) { $num_bytes = $content.Length; }
  76:             $content.SubString($content.Length - $num_bytes);
  77:         }
  78:     }
  79: }
  80:  
  81: Do-Tail -filespec $filespec -num_bytes $num_bytes -num_lines $num_lines `
  82:     -follow $follow -sleep $sleep -quiet $quiet;

 

Download the source for the script here: Tail.ps1

 

Read the original blog entry...

More Stories By Joe Pruitt

Joe Pruitt is a Principal Strategic Architect at F5 Networks working with Network and Software Architects to allow them to build network intelligence into their applications.