Tail in vbScript


I was a little bored last week so I wrote a tail programme in vbScript. It probably isn’t the best language to write it in as it has no support for pointers within a file or the ability to read a file backwards. Unfortunately you have to read the file forwards from the beginning each time it updates which obviously doesn’t scale well for larger files. That said it seemed to work OK for files up to a few million lines off local disk.

The script uses WMI to monitor changes to the file under a folder object. When the file has been modified the script then checks for new lines and shows them to the screen. In this way it emulates tail with follow (tail -f).

'##############################################################################
'# Name: Tail.vbs
'# Description: Tail a file with follow
'# Version History:
'#
'# 1.0 Stuart Jordan Script creation
'#
'##############################################################################

'Grab the path of the file to be tailed from the command line
Set args = WScript.Arguments
if WScript.Arguments.Count <> 1 then
 wscript.echo vbcrlf
 wscript.echo "------------------------------------------------------------"
 wscript.echo " Tail.vbs syntax help "
 wscript.echo "------------------------------------------------------------"
 wscript.echo "You haven't correctly specified the filename to be tailed."
 wscript.echo vbcrlf
 wscript.echo "Syntax:"
 wscript.echo vbcrlf
 wscript.echo vbtab & "cscript /nologo tail.vbs c:\myfolder\myfilename.txt"
 wscript.echo vbcrlf
 wscript.echo "If there are spaces in your folder or filename please"
 wscript.echo "enclose the filename in double quotes"
 wscript.echo "------------------------------------------------------------"
 wscript.quit
end if
strLongFileName = args.Item(0)

arrArgsLine=split(strLongFileName,"\")
strDrive=arrArgsLine(0)
if ubound(arrArgsLine)>1 then
 for i=1 to ubound(arrArgsLine)-1
 strFolder=strFolder & "\\" & arrArgsLine(i)
 next
 strFolder=strFolder & "\\"
end if
strFileName=arrArgsLine(ubound(arrArgsLine))

if strDrive = "" OR strFilename = "" then
 wscript.echo "WARNING: Either the drive or filename was not specified. Please try again."
 wscript.quit
end if

wscript.echo vbcrlf
wscript.echo "----------------------------------------------------------------"
wscript.echo "This is the information that was detected:"
wscript.echo "Full path = " & strLongFileName
wscript.echo "Drive letter = " & strDrive
wscript.echo "File name = " & strFileName
wscript.echo "Folder path = " & strFolder
wscript.echo vbcrlf
wscript.echo "----------------------------------------------------------------"
wscript.echo vbcrlf

'This is the interval with which the WMI events are checked
intInterval = "2"
strComputer = "." 

intLastRunLineinFile=get_NumLinesInFile(strLongFileName)

Set objWMIService = GetObject( "winmgmts:" & _ 
 "{impersonationLevel=impersonate}!\\" & _ 
 strComputer & "\root\cimv2" )
if len(strFolder) then
 strQuery = _
 "Select * From __InstanceOperationEvent" _
 & " Within " & intInterval _
 & " Where Targetinstance Isa 'CIM_DataFile'" _
 & " And TargetInstance.Drive='" & strDrive & "'" _
 & " And TargetInstance.Path='" & strFolder & "'"
else
 strQuery = _
 "Select * From __InstanceOperationEvent" _
 & " Within " & intInterval _
 & " Where Targetinstance Isa 'CIM_DataFile'" _
 & " And TargetInstance.Drive='" & strDrive & "\\'"
end if

Set colEvents = objWMIService. ExecNotificationQuery (strQuery) 
WScript.Echo "Monitoring events...[Ctl-C] to end"
Do 
 Set objEvent = colEvents.NextEvent()
 Set objTargetInst = objEvent.TargetInstance
 Select Case objEvent.Path_.Class 
 Case "__InstanceDeletionEvent" 
 if 0=strComp(objTargetInst.Name,strLongFileName,1) then
 wscript.echo objTargetInst.Name & " has been deleted. Exiting."
 wscript.quit
 end if
 Case "__InstanceModificationEvent" 
 if 0=strComp(objTargetInst.Name,strLongFileName,1) then
 tailFile(objTargetInst.Name)
 end if
 End Select 
Loop

function tailFile(strFileName)
 Const FOR_READING = 1
 dim intCurrLine,intNewNumLines
 Set objFSO = WScript.CreateObject("Scripting.FileSystemObject")
 Set objFile = objFSO.OpenTextFile(strFileName, FOR_READING)

 intNewNumLines=0
 intCurrLine=0
 Do Until objFile.AtEndOfStream
 intCurrLine=intCurrLine+1
 if intCurrLine <= intLastRunLineinFile then
 objFile.skipline
 else
 wscript.echo objFile.ReadLine
 intNewNumLines=intNewNumLines+1
 end if
 Loop

 if intCurrLine < intLastRunLineinFile then 
 'The number of lines on this scan was less than the last run so resetting the num lines in file
 intLastRunLineinFile = intCurrLine
 else
 intLastRunLineinFile=intLastRunLineinFile+intNewNumLines
 end if
 'wscript.echo "(" & intLastRunLineinFile & ")"
 objFile.close
end function

function get_NumLinesInFile(strFileName)
 Const FOR_READING = 1
 Set objFSO = WScript.CreateObject("Scripting.FileSystemObject")
 Set objFile = objFSO.OpenTextFile(strFileName, FOR_READING)
 intTotalNumLines=0
 Do Until objFile.AtEndOfStream
 objFile.skipline
 intTotalNumLines=intTotalNumLines+1
 Loop
 objFile.close
 get_NumLinesInFile=intTotalNumLines
end function

Download the script here

Advertisements
Tagged

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: