See Also edit
- chan event
- the modern way of writing [fileevent]. The arguments and semantics are identical.
- A higher-level channel API
Synopsis edit
- fileevent channelId readable ?script?
- fileevent channelId writable ?script?
Description edit
This command is used to create file event handlers. A file event handler is a binding between a channel and a script, such that the script is evaluated whenever the channel becomes readable or writable. File event handlers are most commonly used to allow data to be received from another process on an event-driven basis, so that the receiver can continue to interact with the user while waiting for the data to arrive. (from fileevent manpage) A specific description of the relations between the notifier (at the heart of the event system) and I/O channels appears in a SourceForged description at http://tcl.sourceforge.net/c-api/notifier.html.Examples edit
- Tcl/Tk examples?, stackoverflow, 2008-10-03
- Bryan Oakley provides a simple example of [fileevent] with some Tk buttons tied to some other programs.
- client/server with fileevent
- shows asynchronous read/write communication over a pipe with fileevent, in a single script.
- Port scanning in tcl
- PT: a demonstration of using fileevent writable
- recursive fileevent accumulator
- NEM: a technique to avoid global variables when maintaining state between event callbacks.
From a post by Paul Duffin: If you want a separate fileevent for stderr and stdout then you will have to use some form of pipe which you can fake by using [open |] on a simple cat.exe file. e.g.
Can one use a similar trick to put input into 'process.exe', when it asks for it on stdin? If that isn't possible, what if I know that process.exe is going to ask me for me for some specific pieces of information (say, 'name <return> password <return>'). Could I pass that in with 'fileevent writable'? -- Vince.CL: No--if I understand the question correctly. However, [exec] does support [exec $myprog << $text], which might be what you're after. It's a great convenience, worth learning, in any case.RS: ... and if you want to send something to a process's stdin, depending on a prompt from that, it looks very much like a job for Expect...Vince: given Expect doesn't exist on Windows, I was hoping that one could at least handle simple cases with [exec $myprog >& $outpipe <& $pipe], and appropriate fileevents on $pipe, $outpipe. (e.g. if I know that $myprog will prompt me with 'password:' couldn't I look for that in a fileevent attached to $outpipe and then immediately 'puts' the password into $pipe?)
Kevin Kenny wrote in comp.lang.tcl:The usual pattern for reading asynchronously from a pipe is something like:
Notice that the case
mfi: Here is how filevent can be used to wait for user input for specified period of time.
Arjen Markus: You should not be tempted to use [fileevent] to read from an ordinary file that is currently being written by another process. The reason (I learned this the hard way, including a longish thread of messages on the newsgroup) is that ordinary files are almost always readable. Hence the file events will trigger very rapidly and the processing of other events (in wish for instance) is compromised, effectively blocking your application. Instead look at Tailing widget for a better way.LV: Indeed, this is one of Tcl's gotchas - a command called fileevent would be thought to work against files. It is unfortunate that some other name was not chosen. Current OSes don't support generating events as new data appears on a disk file. Instead, think of this command as being for open channels for pipes, sockets, etc. - things which have applications on the other end...RJM 2004-07-30: Contrary to operation with ordinary files, [fileevent] provides its intended functionality on ports to the outside world, especially serial ports. That means: an event is triggered only when any characters are in the queue (for input). At this place I'd propose an extra option to fileevent:
CMcC would like to provide a little bit of esoterica. The manual states that an error from a script is handled by [bgerror] (good) and that the corresponding [fileevent] is deleted (also good.) However, in tcl8.5, one can return a -code which is not a TCL_ERROR, and this *also* leads to the deletion of the fileevent. Personally, I think the error handling is being a bit overzealous by treating any non-0 return code as an error.
lexfiend: It seems that fileevent can cause the Tcl event loop to stop working under circumstances that are not well-documented. For instance, I documented my experience in this comp.lang.tcl thread on trying to fileevent over 1021 TCP connections under Linux.Update 2010-08-27: Further investigation reveals that the underlying use of select() on Unix platforms will trigger undefined behavior in the above scenario. See the above thread for more details on what happens under Linux. In short, ensuring that your Tcl program's open FD limit is capped at 1024 under Unix is a Good Thing.
[jmc] - 2012-08-01 13:31:44What is the interest of creating a fileevent channelId readable where channelId represents a file on a disk? To my understanding this handler will *always* fire as the firing criteria is the presence of EOF character in the file (wich to my knowledge can't be absent in regular files - or am I wrong ?). I ask this question because I recently have to work on a simple way for an aplication to detect changes in the content of a text file. When changes occurs in this text file, then the application open a window to show the file's new content (the new content allways erase and replace the former content). I thought filevent command should be the good tool for this monitoring, but "filevent readable" fires when there is no change (but yes the file is readable). So instead I use repeated polling of file mtime on the file of interest.set pipe [open |cat.exe r+] fileevent $pipe readable .stderr.processing. set process [open "|process.exe 2>@$pipe" r+] fileevent $process readable .stdout.processing.RS: NB: On *n*x machines, just say cat. On Windows machines, you have no cat.exe by default, but a number of helpful toolsets provide it, for instance the Cygnus suite.DKF: This is because the underlying device on the other side of stdout and stderr, a terminal in normal circumstances, is the same device at the OS level. On the other hand, if you've got one redirected to a pipe and the other a socket (why? I dunno!) then you'll see a disconnect between the two. It all depends on configuration.
Can one use a similar trick to put input into 'process.exe', when it asks for it on stdin? If that isn't possible, what if I know that process.exe is going to ask me for me for some specific pieces of information (say, 'name <return> password <return>'). Could I pass that in with 'fileevent writable'? -- Vince.CL: No--if I understand the question correctly. However, [exec] does support [exec $myprog << $text], which might be what you're after. It's a great convenience, worth learning, in any case.RS: ... and if you want to send something to a process's stdin, depending on a prompt from that, it looks very much like a job for Expect...Vince: given Expect doesn't exist on Windows, I was hoping that one could at least handle simple cases with [exec $myprog >& $outpipe <& $pipe], and appropriate fileevents on $pipe, $outpipe. (e.g. if I know that $myprog will prompt me with 'password:' couldn't I look for that in a fileevent attached to $outpipe and then immediately 'puts' the password into $pipe?)
Kevin Kenny wrote in comp.lang.tcl:The usual pattern for reading asynchronously from a pipe is something like:
proc isReadable { f } { # The channel is readable; try to read it. set status [catch { gets $f line } result] if { $status != 0 } { # Error on the channel puts "error reading $f: $result" set ::DONE 2 } elseif { $result >= 0 } { # Successfully read the channel puts "got: $line" } elseif { [eof $f] } { # End of file on the channel puts "end of file" set ::DONE 1 } elseif { [fblocked $f] } { # Read blocked. Just return } else { # Something else puts "can't happen" set ::DONE 3 } } # Open a pipe set fid [open "|ls"] # Set up to deliver file events on the pipe fconfigure $fid -blocking false fileevent $fid readable [list isReadable $fid] # Launch the event loop and wait for the file events to finish vwait ::DONE # Close the pipe close $fid
Notice that the case
# Something else puts "can't happen" set ::DONE 3diLampedusa: on Kenny's example CAN happen. gets can return -1 in non-blocking mode, if the input line was not yet completed with a end-of-line, and is a valid result.NEM 2010-01-20: In this case, fblocked will return 1, so the code is correct -- the previous branch of the if will be selected.
mfi: Here is how filevent can be used to wait for user input for specified period of time.
proc gets-with-timeout {} { global GetsInput set id [after 1200 {puts "Are you fall asleep?"; exit}] fileevent stdin readable {gets stdin GetsInput} vwait ::GetsInput after cancel $id set data $GetsInput uplevel #0 "unset GetsInput" return $data }Setok Note that this is not really complete. What if other file events have been set up for the channel? You need to fetch the old fileevent and then reset it. See getsBG for how I tried this.
Arjen Markus: You should not be tempted to use [fileevent] to read from an ordinary file that is currently being written by another process. The reason (I learned this the hard way, including a longish thread of messages on the newsgroup) is that ordinary files are almost always readable. Hence the file events will trigger very rapidly and the processing of other events (in wish for instance) is compromised, effectively blocking your application. Instead look at Tailing widget for a better way.LV: Indeed, this is one of Tcl's gotchas - a command called fileevent would be thought to work against files. It is unfortunate that some other name was not chosen. Current OSes don't support generating events as new data appears on a disk file. Instead, think of this command as being for open channels for pipes, sockets, etc. - things which have applications on the other end...RJM 2004-07-30: Contrary to operation with ordinary files, [fileevent] provides its intended functionality on ports to the outside world, especially serial ports. That means: an event is triggered only when any characters are in the queue (for input). At this place I'd propose an extra option to fileevent:
- -minsize n
- -match string
CMcC would like to provide a little bit of esoterica. The manual states that an error from a script is handled by [bgerror] (good) and that the corresponding [fileevent] is deleted (also good.) However, in tcl8.5, one can return a -code which is not a TCL_ERROR, and this *also* leads to the deletion of the fileevent. Personally, I think the error handling is being a bit overzealous by treating any non-0 return code as an error.
lexfiend: It seems that fileevent can cause the Tcl event loop to stop working under circumstances that are not well-documented. For instance, I documented my experience in this comp.lang.tcl thread on trying to fileevent over 1021 TCP connections under Linux.Update 2010-08-27: Further investigation reveals that the underlying use of select() on Unix platforms will trigger undefined behavior in the above scenario. See the above thread for more details on what happens under Linux. In short, ensuring that your Tcl program's open FD limit is capped at 1024 under Unix is a Good Thing.
arjen 2012-08-01 14:06:14:For files on disk, [fileevent] is indeed not useable (so the chosen name is not quite apt). The solution you chose seems a reasonable one.RLE 2012-08-01: If you are running under Linux you can use the tcl-inotify extension to watch, in an event driven manner, for changes to the file on disk.