tail -f under a pipe

2 views
Skip to first unread message

Clement Ayme

unread,
Mar 20, 2000, 3:00:00 AM3/20/00
to

HI all,

What I try to do is very basic.
(I want to get data by the tail -f UNIX command from
a file written by another process)

I set an open pipe as:

set fh0 [open "|tail -f $logfile" "r"]
fileevent $fh0 readable "local $fh0"

then configure it to be non-blocking and buffered to 1 line

fconfigure $fh0 -blocking 0
fconfigure $fh0 -buffering line

This is done in a special proc


Later in the process in another proc I want to kill this pipe with a
close command
but the problem is that this close command will never return until
a data is written to the pipe.

I don't know what could be the way to do that.

Help if you can,

THANKS ALL,


--
Mobile: +33/684424362 mailto:ca...@techtonic.com
Protect privacy, boycott Intel: http://www.bigbrotherinside.org
----------------------------------------------------------------------


Scott Redman

unread,
Mar 20, 2000, 3:00:00 AM3/20/00
to
Why not do this:

set fh0 [open $logfile "r"]


fileevent $fh0 readable {local $fh0}

fconfigure $fh0 -blocking 0 -buffering line

Then:

close $fh0

The problem is that closing the pipe in your code doesn't have any affect on
"tail" until it tries to write to its stdout. If it does not write to
stdout, it will not see the closed pipe. You could get the pid of the tail
command and kill it off that way, but why not use the fileevents on the log
file directly from Tcl?

-- Scott


"Clement Ayme" <ca...@tif.ti.com> wrote in message
news:38D66DA7...@tif.ti.com...

Clement Ayme

unread,
Mar 21, 2000, 3:00:00 AM3/21/00
to
OK,

I change my code regarding to your advices but what I get is an

infinite loop because it seems that the fileevent readable does not

works as I expect.

It executes the script even is no data is readable !

So , what is the right way to emulates the UNIX tail -f command ?

Thanks all.


Gordon Johnstone

unread,
Mar 21, 2000, 3:00:00 AM3/21/00
to

I was given this


set fp [open $logfile]
seek $fp 0 end
while 1 {
gets $fp line
if [string length $line] {
puts $line
} {
after 1000
}
}

Clement Ayme

unread,
Mar 21, 2000, 3:00:00 AM3/21/00
to
OK,

But I forgot to tell that I need to do this in a non blocking form.

The other widgets must operate in foreground.

Something is missing to me in the fileevent keyword.

The manpages saids:

A channel is considered to be readable if there is unread data
available on the underlying device.
A channel is also considered to be readable if there is unread data in an input buffer,
except in the special case where the most recent attempt to read from the channel
was a gets call that could not find a complete line in the input buffer.
This feature allows a file to be read a line at a time in nonblocking mode using events.
A channel is also considered to be readable if an end of file or error condition
is present on the underlying file or device. It is important for script to check
for these conditions and handle them appropriately; for example,
if there is no special check for end of file, an infinite loop may occur
where script reads no data, returns, and is immediately invoked again.

Even if I try to have a check on the eof, this is never asserted.

I have been looking deeply in this newsgroup and many useful

web links but without great success.

Others ideas will be welcome.

Many thanks

Victor Wagner

unread,
Mar 21, 2000, 3:00:00 AM3/21/00
to
Scott Redman <red...@nospam.scriptics.com> wrote:
: Why not do this:

: set fh0 [open $logfile "r"]
: fileevent $fh0 readable {local $fh0}

Hmm, Fileevents in Tcl never worked on regular files. Just becouse
underlying system doesn't provide suitable notification mechanism.
Has something changed in 8.3?

I have Tcl-only tail implementation on my home page (some where near
http://www.ice.ru/~vitus/works/tcl.html), but it is implemnented using
after. /usr/bin/tail does the same.

: fconfigure $fh0 -blocking 0 -buffering line

: Then:

: close $fh0

: The problem is that closing the pipe in your code doesn't have any affect on
: "tail" until it tries to write to its stdout. If it does not write to
: stdout, it will not see the closed pipe. You could get the pid of the tail

Here you should write

using [pid $fh0]

: command and kill it off that way, but why not use the fileevents on the log
: file directly from Tcl?

:>
:> What I try to do is very basic.


:> (I want to get data by the tail -f UNIX command from
:> a file written by another process)
:>
:> I set an open pipe as:
:>
:> set fh0 [open "|tail -f $logfile" "r"]
:> fileevent $fh0 readable "local $fh0"
:>
:> then configure it to be non-blocking and buffered to 1 line
:>
:> fconfigure $fh0 -blocking 0
:> fconfigure $fh0 -buffering line
:>
:> This is done in a special proc
:>
:>
:> Later in the process in another proc I want to kill this pipe with a
:> close command
:> but the problem is that this close command will never return until
:> a data is written to the pipe.
:>
:> I don't know what could be the way to do that.
:>
:> Help if you can,
:>
:> THANKS ALL,

:>
:>
:> --


:> Mobile: +33/684424362 mailto:ca...@techtonic.com
:> Protect privacy, boycott Intel: http://www.bigbrotherinside.org
:> ----------------------------------------------------------------------

:>
:>
:>

--
И республиками правят голые короли.
--- С.Е. Лец

Gordon Johnstone

unread,
Mar 22, 2000, 3:00:00 AM3/22/00
to
Clement Ayme wrote:
>
> OK,
>
> But I forgot to tell that I need to do this in a non blocking form.
>
> The other widgets must operate in foreground.
>
> Something is missing to me in the fileevent keyword.
>
I don't think you need fileevent here, it doesn't seem applicable (
correct me if I'm wrong guys ) as you have to have polling.

This works:-
#!/bin/sh
#\
exec tclsh8.3 "$0" ${1+"$@"}

variable fp;set fp ""

proc tail {file} {
if {$::fp == ""} {
set ::fp [open $file]
seek $::fp 0 end
}
gets $::fp line


if [string length $line] {
puts $line

after 10 [list tail $file]
} {
after 1000 [list tail $file]

}
}

proc dosomethingelse {} {
puts "I'm doing something else"
after 100 dosomethingelse
}
after 10 [list tail /tmp/log]
after 10000 dosomethingelse
vwait forever

Donal K. Fellows

unread,
Mar 22, 2000, 3:00:00 AM3/22/00
to
In article <38D73F23...@tif.ti.com>,

Clement Ayme <ca...@tif.ti.com> wrote:
> I change my code regarding to your advices but what I get is an
> infinite loop because it seems that the fileevent readable does not
> works as I expect. It executes the script even is no data is
> readable!

Yes, since a read from a regular file never blocks. (The details of
networked file systems are usually glossed over...)

> So, what is the right way to emulates the UNIX tail -f command?

You check the size of the file periodically. It is quite easy to set
such a polling scheme up using Tcl...

# Watch the file $file and call $callback with a single extra
# argument (the read data) whenever new data becomes available.
# WARNING: Only ever run a maximum of one tail per file.
proc tail {file callback {delay 500}} {
global tailevents tailfids
set fid [open $file]
# Hmm. Maybe some [fconfigure]ing is needed here...
set size [file size $file]
if {$size > 0} {
uplevel #0 $callback [list [read $fid $size]]
}
set tailevents($file) [after $delay [list \
tail'event $fid $file $size $callback $delay]]
set tailfids($file) $fid
}
# Cancel the tail on the given file
proc tailCancel {file} {
global tailevents tailfids
if {[info exist tailevents($file)]} {
after cancel $tailevents($file)
close $tailfids($file)
unset tailevents($file) tailfids($file)
}
}
# Internal timer callback.
proc tail'event {fid file size callback delay} {
global tailevents
set ns [file size $file]
if {$ns > $size} {
uplevel #0 $callback [list [read $fid [expr {$ns-$size}]]]
set size $ns
}
set tailevents($file) [after $delay [list \
tail'event $fid $file $size $callback $delay]]
}

Donal.
--
Donal K. Fellows http://www.cs.man.ac.uk/~fellowsd/ fell...@cs.man.ac.uk
-- The small advantage of not having California being part of my country would
be overweighed by having California as a heavily-armed rabid weasel on our
borders. -- David Parsons <o r c @ p e l l . p o r t l a n d . o r . u s>

Victor Wagner

unread,
Mar 23, 2000, 3:00:00 AM3/23/00
to
Gordon Johnstone <gdjoh...@scs.dera.gov.uk> wrote:

: I was given this


: set fp [open $logfile]
: seek $fp 0 end
: while 1 {

: gets $fp line


: if [string length $line] {
: puts $line
: } {

: after 1000
: }
: }

I've already suggested to add to FAQ:
"Never use while 1 {} in Tk script"

Just changing this script to two-argument form of after and all would be
fine. Only you should check result of gets rather than length of line -
I see no reason why empty line shouldn't be possible in input file

proc getline {fp} {
while {[gets $fp line]>=0} {
puts $line
}
after 1000 [list getline $fp]
}

set fp [open $logfile]
seek $fp 0 end

getline $fp

And no playing with fconfigure - it is irrelevant to regular files.

--
We come to bury DOS, not to praise it.
-- Paul Vojta, vo...@math.berkeley.edu

Sami Tikka

unread,
Mar 28, 2000, 3:00:00 AM3/28/00
to
You were looking into implementing the Unix 'tail' command in Tcl? Take a
look at TkView pager and file viewer (http://www.iki.fi/sti/tkview.html)
--
Sami Tikka, s...@iki.fi, http://www.iki.fi/sti/
"There is no spoon."

Reply all
Reply to author
Forward
0 new messages