The bgexec command executes Unix commands in the background, allowing Tk to handle events. A global Tcl variable varName is set when the command 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 the application is waiting for du to finish, Tk is not handling X events.
The bgexec command performs the same functions as exec , but also allows Tk to handle events. You can execute a long-running Unix command and the Tk widgets will behave normally. When the command finishes, its output and the exit status are written to Tcl variables. This makes it easy to monitor and save the output of a command.
global
myStatus myOutput
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 the du command has completed. MyStatus will contain the command's exit status. MyOutput , specified by the -output option, will store the output of the command.
You can also terminate the
command by setting the variable myStatus . If myStatus is set before du
has completed, the process is killed by sending a configurable Unix signal
(by default it's SIGKILL). It makes no difference what myStatus is set
to.
set myStatus {}
There are other bgexec options to collect different
types of information.
global myStatus myOutput myErrs
bgexec myStatus
-output myOutput -error myErrs du -s $dir
The -error option is similar to -output . It sets a global variable when the command completes. The variable will contain any data written to stderr by the command.
The -output and
-error variables are written to only after the command completes. If the
command takes a long time, 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
}
bgexec myStatus -onoutput
GetInfo du -s $dir
When output is available, the procedure GetInfo is called. The nerror option performs a similar function for the stderr data stream.
Like exec , bgexec returns an error if the exit code of the Unix
command 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 { bgexec myStatus
-output myOutput du -s $dir }
By default, bgexec will wait for the command
to finish. But you can detach the command by adding an ampersand (&) to
the end of the command line.
global myStatus myOutput
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 command to finish, you can use tkwait . When the command
finishes, the variable myStatus will be written to, breaking the tkwait
loop.
global myStatus myOutput
bgexec myStatus -output myOutput du -s
$dir &
...
tkwait variable myStatus
bgexec varName ?option value ?... command ?arg ?...
VarName is the name of a global variable which is set when the designated Unix command has finished executing. The exit status of the command will be stored in varName . The exit status is a list of a status token, the process-id of the command, the exit code, and a status message. You can also prematurely terminate the command by setting varName . The command will be sent a signal to terminate it (by default the signal is a SIGKILL; see the -killsignal option).
Command is the name of the Unix command to be executed and args are any extra arguments for command . Command and args may be in any form accepted by exec . (See the exec manual for further information.) If the last argument is an ampersand (&), the command will be run detached, and bgexec will return immediately. VarName will still be set with the return status when command 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 in these situations, temporarily preventing user interaction. See the busy manual for details.
Execute the Unix command 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
}
}
While this works with the above example, but there are some important differences.
The biggest advantage of bgexec is that it requires no additional Tcl code to run a Unix command. It's simpler, and therefore there's less chance of errors being introduced.
Bgexec also handles things that fileevent can not. For example, you can't get back the exit status of the command. In the code above, we're assuming that the command has completed once stdout is closed. The problem is that some commands, like compress , reopen stdout, which fool fileevent . We're also assuming that the Unix command will write its output line-by-line. Running another command, your application may block in the gets command, reading a partial line. Conversely, bgexec handles non-blocking I/O tranparently for you. Finally, since data collection is handled in C code, bgexec is faster, getting you back to the Tk event loop more quickly.