Vahagn
Vahagn

Reputation: 4850

How to track a log file in TCL

Say there is a file log.txt and some kind of log is being appended to it permanently.

I want to track that file in the TCL environment.

I have tried this but it didn't worked.

set log [open log.txt a]

for { } { true } { update; after 1000 } {

    # expected to get here the appended part
    read $log

    seek $log 0 end

}

Is it possible to read the modified file by the same file handle log, or I have to close and re-open the file log.txt ?

Is there a kind of equivalent of Linux command tail -f in TCL ?

Upvotes: 6

Views: 3427

Answers (3)

GrAnd
GrAnd

Reputation: 10221

I have managed to write tail -f equivalent on pure Tcl. The code is below:

proc readHandler {handle} {
    puts -nonewline [read $handle]
    flush stdout
    # to reduce CPU overhead...
    after 1000
}

set hFile [open "file.log" "r"]
# seek to the end of file if needed
# seek $hFile 0 end 

fconfigure $hFile -buffering none -eofchar "" -blocking no
fileevent $hFile readable [subst {readHandler $hFile}]

#just for exiting into main event loop
vwait forever

Upvotes: 0

Colin Macleod
Colin Macleod

Reputation: 4382

You're not far off, just a few things to fix:

  1. In this code you are reading from the file, not appending to it, some other process will be doing that, so should open the file in read mode, not append mode, ie. drop the "a" option from the open command.
  2. You should only seek to the end of the file once, before you start reading, otherwise you will be skipping stuff that gets added later, so move the seek command ahead of the loop.
  3. In the code posted, you are not doing anything with the text that you read - I'll give an example of just writing it back to stdout.
  4. You only need the update command if your processing within the loop involves updating a gui or something else that requires the event loop - it's not needed for the code posted. Altogether this gives:

    set log [open log.txt]
    seek $log 0 end
    
    for { } { true } { after 1000 } {
    
        # expected to get here the appended part
        puts -nonewline [read $log]
    }
    

Upvotes: 0

kostix
kostix

Reputation: 55533

Just use tail. It knows more that you about how to handle complicated cases (you can look at its source).

In one of my projects, I have something like this to monitor a trace file produced by a proprietary tool:

set fd [open [list | tail --follow=name --retry --lines 0 $opt(trace) 2>@1]]
chan event $fd readable [list FollowTrace $fd]

proc FollowTrace fd {
  if {[gets $fd line] < 0} {
    set code [catch {close $fd} err]
    if {$code == 0} {
      set ::result 0
    } else {
      puts stderr $err
      set ::result 1
    }
    return
  }

  switch -regexp -matchvar parts -- $line {
    {Tm_Session::Open.*FileName=([^,]+)} {
       TryMakeLock [FullPathname [lindex $parts 1]]
     }
     {Tm_Session::Close.*FileName=([^,]+)} {
        StartUpload [lindex $parts 1]
     }
  }
}

The general idea is that you spawn tail on a given file then enter event loop and process tail's output line-by-line.

Upvotes: 4

Related Questions