- fileevent channelId readable ?script?
- fileevent channelId writable ?script?
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.No--if I [CL] 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 adds that, 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 the comp.lang.tcl newsgroup:The usual pattern for reading asynchronously from a pipe is something like:
Notice that the case
client/server with fileevent is an example showing asynchronous read/write communication over a pipe with fileevent, in a single script.
Here is how filevent can be used to wait for user input for specified period of time. --mfi.
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:
See Port scanning in tcl for a demonstration of using fileevent writable. PTNEM - see recursive fileevent accumulator for a technique to avoid global variables when maintaining state between event callbacks.
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.
set pipe [open |cat.exe r+]
fileevent $pipe readable .stderr.processing.
set process [open "|process.exe 2>@$pipe" r+]
fileevent $process readable .stdout.processing.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. (RS)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.No--if I [CL] 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 adds that, 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 the comp.lang.tcl newsgroup: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 $fidNotice that the case
# Something else
puts "can't happen"
set ::DONE 3on 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. --diLampedusaNEM 2010-01-20: In this case, fblocked will return 1, so the code is correct -- the previous branch of the if will be selected.client/server with fileevent is an example showing asynchronous read/write communication over a pipe with fileevent, in a single script.
Here is how filevent can be used to wait for user input for specified period of time. --mfi.
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
See Port scanning in tcl for a demonstration of using fileevent writable. PTNEM - see recursive fileevent accumulator for a technique to avoid global variables when maintaining state between event callbacks.
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.
