proc tk_exec_fileevent {id} {
global tk_exec_data
global tk_exec_cond
global tk_exec_pipe
if {[eof $tk_exec_pipe($id)]} {
fileevent $tk_exec_pipe($id) readable ""
set tk_exec_cond($id) 1
return
}
append tk_exec_data($id) [read $tk_exec_pipe($id) 1024]
}
proc tk_exec {args} {
global tk_exec_id
global tk_exec_data
global tk_exec_cond
global tk_exec_pipe
global tcl_platform
global env
if {![info exists tk_exec_id]} {
set tk_exec_id 0
} else {
incr tk_exec_id
}
set keepnewline 0
for {set i 0} {$i < [llength $args]} {incr i} {
set arg [lindex $args $i]
switch -glob -- $arg {
-keepnewline {
set keepnewline 1
}
-- {
incr i
break
}
-* {
error "unknown option: $arg"
}
* {
break
}
}
}
if {$i > 0} {
set args [lrange $args $i end]
}
if {$tcl_platform(platform) == "windows" && \
[info exists env(COMSPEC)]} {
set args [linsert $args 0 $env(COMSPEC) "/c"]
}
set pipe [open "|$args" r]
set tk_exec_pipe($tk_exec_id) $pipe
set tk_exec_data($tk_exec_id) ""
set tk_exec_cond($tk_exec_id) 0
fconfigure $pipe -blocking 0
fileevent $pipe readable "tk_exec_fileevent $tk_exec_id"
vwait tk_exec_cond($tk_exec_id)
if {$keepnewline} {
set data $tk_exec_data($tk_exec_id)
} else {
set data [string trimright $tk_exec_data($tk_exec_id) \n]
}
unset tk_exec_pipe($tk_exec_id)
unset tk_exec_data($tk_exec_id)
unset tk_exec_cond($tk_exec_id)
if {[catch {close $pipe} err]} {
error "pipe error: $err"
}
return $data
}
Some issues:- A "background" exec (with &) is not handled yet.
- The case that stdout is redirected is not handled yet.
# --- patch begin ---
--- tk_exec.tcl 2006-11-03 19:12:04.000000000 +0100
+++ tk_exec_old.tcl 2006-11-03 19:18:34.000000000 +0100
@@ -64,20 +64,21 @@
set tk_exec_data($tk_exec_id) ""
set tk_exec_cond($tk_exec_id) 0
+ set id $tk_exec_id
fconfigure $pipe -blocking 0
- fileevent $pipe readable "tk_exec_fileevent $tk_exec_id"
+ fileevent $pipe readable "tk_exec_fileevent $id"
- vwait tk_exec_cond($tk_exec_id)
+ vwait tk_exec_cond($id)
if {$keepnewline} {
- set data $tk_exec_data($tk_exec_id)
+ set data $tk_exec_data($id)
} else {
- set data [string trimright $tk_exec_data($tk_exec_id) \n]
+ set data [string trimright $tk_exec_data($id) \n]
}
- unset tk_exec_pipe($tk_exec_id)
- unset tk_exec_data($tk_exec_id)
- unset tk_exec_cond($tk_exec_id)
+ unset tk_exec_pipe($id)
+ unset tk_exec_data($id)
+ unset tk_exec_cond($id)
if {[catch {close $pipe} err]} {
error "pipe error: $err"
# --- patch end ---Thank you for this work ! Stefan ([email protected])Dave Snyderman points out that Brent's book [1] includes an ExecLog that does much of the same.
The BLT extension has a command bgexec which is similar. Example code from the BLT docs (slightly adapted):
proc widgetwrite { data } {
# Put your output-handling code here, e.g.
.t insert end $data
}
global myStatus
blt::bgexec myStatus -onoutput widgetwrite du -s $dirTk will continue to handle events while the external command is running; whenever the command writes to its stdout, the output is delivered by a call to the proc named by "-onoutput": no need to wait for the command to complete.FPX: I just made a change to add "command /c" before the command to be executed if the platform is windows. I got weird results (hangs) in Windows otherwise. - RS: Note however that command.com is the weaker DOS shell - on Win NT/2K you have cmd.exe (but command.com is still there); the default shell can be found in ::env(COMSPEC). - FPX: okay, so I've replaced "command" with env(COMSPEC) above.DGP You ought to take one more step and make use of auto_execok.
[PFV]: In order to get the exit status of the exec'd command, the channel needs to be configured back to blocking mode before closing the pipe, like:
+ fconfigure $pipe -blocking 1
if {[catch {close $pipe} err]} {
error "pipe error: $err"See also:

