This is a more efficient implementation of the tailf functionality based on the tcl-inotify package. It has the advantage that it doesn't suffer from polling overhead as the other implementations. Inotify asynchronously informs tailf of write changes in a file.
#!/bin/tclsh
# An efficient tailf implementation using inotify
package require inotify
package provide tailf
namespace eval tailf {
namespace export add rem
array set File2wd {}
array set Wd2info {}
inotify create ::tailf::watch ::tailf::handler
proc getpos {filepath} {
set fd [open $filepath r]
seek $fd 0 end
set pos [tell $fd]
close $fd
return $pos
}
proc handler {args} {
variable File2wd
variable Wd2info
set events [watch read]
foreach {wd flags cookie filepath} $events {
if {! [info exists Wd2info($wd)]} {
return
}
set diff {}
set callback [lindex $Wd2info($wd) 1]
set filepath [lindex $Wd2info($wd) 2]
set old [lindex $Wd2info($wd) 0]
set new [getpos $filepath]
set Wd2info($wd) [lreplace $Wd2info($wd) 0 0 $new]
if {$new < $old} {
set old 0
}
if {$old != $new} {
set fd [open $filepath r]
seek $fd $old
while {[gets $fd line] >= 0} {
append diff "$line" "\n"
}
close $fd
}
eval [list $callback $filepath $diff]
}
return
}
proc add {filepath callback} {
variable File2wd
variable Wd2info
if {[file exists $filepath] && [file isfile $filepath]} {
set pos [getpos $filepath]
set wd [watch add $filepath {w}]
set File2wd($filepath) $wd
set Wd2info($wd) [list $pos $callback $filepath]
} else {
error "is either not a file, or does not exist"
}
}
proc rem {filepath} {
variable File2wd
variable Wd2info
if {! [info exists ::tailf::File($filepath)]} {
error "$filepath is not monitored, or watch has been removed"
}
set wd $File2wd($filepath)
unset Wd2info($wd)
unset File2wd($filepath)
watch del $filepath
}
} ;# namespace endproc puts_hdlr {filepath diff} {
puts -nonewline $diff
}
::tailf::add "delme.txt" puts_hdlr
set a {}
vwait afr - 2010-09-29 08:46:34
inotify 1.3 with 8.6b1.1 requires changes in proc handler
set events [join [watch read]]
foreach {k wd k flags k cookie k filepath} $events {