The bgexec command executes programs in the background, allowing Tk to handle events. A global Tcl variable varName is set when the program has completed.
set out [exec du -s $dir]
puts "Disk usage for $dir is $out"
While du is running, scrollbars won't respond. None of the Tk widgets will be redrawn properly. The send command won't work. And the worst part is that the application appears hung up or dead. The problem is that while exec is waiting for du to finish, Tk is not able to handle X events.
The bgexec command performs the same functions as exec, but also allows Tk to handle events. You can execute a long-running program and the Tk widgets will behave normally. When the program finishes, its output and the exit status are written to Tcl variables. This makes it easy to monitor and save the output of a program.
global myStatus myOutput
blt::bgexec myStatus -output myOutput du -s $dir
puts "Disk usage for $dir is $myOutput"
Two global variables, myStatus and myOutput, will be set by bgexec when du has completed. MyStatus will contain the program's exit status. MyOutput, specified by the -output option, will store the output of the program.
You
can also terminate the program by setting the variable myStatus. If myStatus
is set before du has completed, the process is killed. Under Unix, this
is done sending by a configurable signal (by default it's SIGKILL). Under
Win32, this is done by calling TerminateProcess. It makes no difference
what myStatus is set to.
set myStatus {}
There are several bgexec options to collect different types of information.
global myStatus myOutput myErrs
blt::bgexec myStatus -output myOutput -error myErrs du -s $dir
The -error option is similar to -output. It sets a global variable when the program completes. The variable will contain any data written to stderr by the program.
The -output and -error variables are set only after the program
completes. But if the program takes a long time, to run you may want to
receive its partial output. You can gather data as it becomes available
using the -onoutput option. It specifies a Tcl command prefix. Whenever
new data is available, this command is executed, with the data appended
as an argument to the command.
proc GetInfo { data } {
puts $data
}
blt::bgexec myStatus -onoutput GetInfo du -s $dir
When output is available, the procedure GetInfo is called. The -onerror option performs a similar function for the stderr data stream.
Like exec, bgexec
returns an error if the exit code of the program is not zero. If you think
you may get a non-zero exit code, you might want to invoke bgexec from within
a catch.
catch { blt::bgexec myStatus -output myOutput du -s $dir }
By default, bgexec will wait for the program to finish. But you can detach
the program making ampersand (&) the last argument on the command line.
global myStatus myOutput
blt::bgexec myStatus -output myOutput du -s $dir &
Bgexec will return immediately and its result will be a list of the spawned
process ids. If at some point you need to wait for the program to finish
up, you can use tkwait. When the program finishes, the variable myStatus
will be written to, breaking out the tkwait command.
global myStatus myOutput
blt::bgexec myStatus -output myOutput du -s $dir &
...
tkwait variable myStatus
Syntax The bgexec command takes the following form:
blt::bgexec varName ?option value?... program ?arg?...
VarName is the name of a global variable which is set when program has finished executing. The exit status of will be stored in varName. The exit status is a list of a status token, the process-id of the program, the exit code, and a status message. You can also prematurely terminate the program by setting varName. Under Unix, the program will be sent a signal to terminate it (by default the signal is a SIGKILL; see the -killsignal option).
Program is the name of the program to be executed and args are any extra arguments for program. The syntax of program and args is the same as the exec command. So you can redirect I/O, execute pipelines, etc. (see the exec manual for further information) just like exec. If the last argument is an ampersand (&), the program will be run detached, and bgexec will return immediately. VarName will still be set with the return status when program completes.
Care must be taken to prevent an application from preempting itself by blocking further user-interactions (such as button clicks). The BLT busy command is very useful for just these situations. See the busy manual for details.
Execute the program
with the open command (using the "|" syntax) and save the file handle.
global fileId
set fileId [open "|du -s $dir" r]
Next register a Tcl code snippet with fileevent to be run whenever output
is available on the file handle. The code snippet will read from the file
handle and save the output in a variable.
fileevent fileId readable {
if { [gets $fileId line] < 0 } {
close $fileId
set output $temp
unset fileId temp
} else {
append temp $line
}
}
The biggest advantage of bgexec is that, unlike fileevent, it requires no additional Tcl code to run a program. It's simpler and less error prone. You don't have to worry about non-blocking I/O. It's handled transparently for you.
Bgexec runs programs that fileevent can not. Fileevent assumes that the when stdout is closed the program has completed. But some programs, like the Unix compress program, reopen stdout, fooling fileevent into thinking the program has terminated. In the example above, we assume that the program will write and flush its output line-by-line. However running another program, your application may block in the gets command reading a partial line.
Bgexec lets you get back the exit status of the program. It also allows you to collect data from both stdout and stderr simultaneously. Finally, since data collection is handled in C code, bgexec is faster. You get back to the Tk event loop more quickly, making your application seem more responsive.