blt::bgexec(3tcl)

🌸 NAME 🌸

bgexec β€” run programs in the background while still handling Tk events. And kill β€” terminate a program or send it a signal. Two lil besties, one manpage πŸ’…


✨ SYNOPSIS ✨

blt::bgexec varName ?switches? program ?arg?...
blt::kill processid ?signal?

πŸ¦‹ DESCRIPTION πŸ¦‹

Okay so first up, the kill command: it terminates a processid, or, under Unix, sends a signal. That's it, that's the whole vibe πŸ’€

Now the main character: bgexec. It executes a program pipeline using the Tcl event-loop, which means other events can keep on being serviced while it's doing its thing. No freezing, no hanging, no "why is my whole app stuck" 😭

When it's done, it sets the global variable varName to a list of 4 status values: a text token, the process-id, the exit code, and a text message. So basically it gives you the whole receipts πŸ“

Bgexec has capabilities similar to the exec command, but with the added support for callbacks, output to variables, and termination. It's giving exec but with ✨extras✨.

When you use it with no options, the value returned from bgexec is the output from the program. But when the last arg is an ampersand (&), the program runs detached, and bgexec immediately returns with a list of the process ids created in the command pipeline. Detached processes can be interrupted and terminated simply by setting varName. Iconic behavior honestly πŸ‘‘

The valid switches are as follows:

  • -check num β€” Interval in ms to poll for the exiting processes. The default is 1000.
  • -closeonkill millisecs β€” Force close of stdin/stdout on kill after the given interval. This lets kill finalize processes, even uninterruptably sleeping ones that are unable to receive signals. The default is 0, for do not force close.
  • -command script β€” Specifies a command to call upon command completion/termination. Two extra arguments are appended before the call: the data output from the command, and the status info as set into varName.
  • -decodeerror encodingName β€” Specifies the encoding of the stderr channel. This affects only data returned to the Tcl interpreter. No translation is done on file redirection. For example, if data is to be converted from Unicode for use in Tcl, you would use the "unicode" encoding. The default is that no translation is performed.
  • -decodeoutput encodingName β€” Specifies the encoding of the stdout channels. This affects only data returned to the Tcl interpreter. No translation is done on file redirection. For example, if data is to be converted from Unicode for use in Tcl, you would use the "unicode" encoding. The default is that no translation is performed.
  • -echo boolean β€” Indicates if the pipeline's stderr stream should be echoed. Note: this option is deprecated.
  • -error varName β€” Specifies that a global variable varName is to be set with the contents of stderr after the program has completed.
  • -keepnewline boolean β€” Specifies that a trailing newline should be retained in the output. If boolean is true, the trailing newline is truncated from the output of the -onoutput and -output variables. The default value is true.
  • -killsignal signal β€” Specifies the signal to be sent to the program when terminating. This option is available only on Unix. signal can either be a number (typically 1-32) or a mnemonic (such as SIGINT). If signal is the empty string, then no signal is sent. The default signal is 9 (SIGKILL). 😳
  • -lasterror varName β€” Specifies a variable varName that is updated whenever data becomes available from standard error of the program. varName is a global variable. Unlike the -error option, data is available as soon as it arrives.
  • -lastoutput varName β€” Specifies a variable varName that is updated whenever data becomes available from standard output of the program. varName is a global variable. Unlike the -output option, data is available as soon as it arrives.
  • -limit numBytes β€” Limit the size of the returned data to numBytes, terminating the program if exceeded. The limit applies to both stdout and stderr.
  • -linebuffered boolean β€” Specifies that updates should be made on a line-by-line basis. Normally, when new data is available, bgexec will set the variable (the -lastoutput and -lasterror options) or invoke the command (the -onoutput and -onerror options), delivering all the new data currently available. If boolean is true, only one line at a time will be delivered. This can be useful when you want to process the output on a line-by-line basis. The default value is false.
  • -local boolean β€” When boolean is true, any unqualified variables or command options are treated as local to the current namespace. This is mostly useful for non-detaching (no ampersand) commands. Note that using this flag with a detached command will use variables from the current namespace, not from the current proc stack-frame. Sneaky lil difference, keep an eye on it πŸ‘€
  • -onerror command β€” Specifies the start of a Tcl command that will be executed whenever new data is available from standard error. The data is appended to the command as an extra argument before it is executed.
  • -onoutput command β€” Specifies the start of a Tcl command that will be executed whenever new data is available from standard output. The data is appended to the command as an extra argument before it is executed.
  • -output varName β€” Specifies a global variable varName to be set with the output of the program, upon completion.
  • -raise boolean β€” When boolean is true, a non-zero return code from a non-detached command will raise an error (i.e. it emulates exec). The default is false; an error is generated only if one of the following occurs: invalid options are given, a redirection error, or process creation failure (eg. executable program not found). Detached commands, of course, never raise an error on a non-zero return code.
  • -- β€” This marks the end of the options. The following argument will be considered the name of a program even if it starts with a dash (-).

🌈 USAGE 🌈

Invoking bgexec without a trailing ampersand will block and wait for the result. But β€” and this is the good part β€” other Tcl events continue to be serviced. This prevents Tcl from hanging, eg:

pack [text .t]
set val [blt::bgexec myStatus du -s]

Note that text widget .t continues to respond to events. See? Not stuck, just patient πŸ’–


πŸŽ€ CALLBACKS πŸŽ€

Here's an example that invokes the Unix du program with a -command callback:

proc Done {data status} {  puts "Done($status)\n$data" }

blt::bgexec myStatus  -command Done   du -s $dir &

When du has completed, the handler Done is called with data and status. Also, the global variable myStatus is set to contain the program's exit status, eg:

EXITED 26811 0 {child completed normally}

If myStatus is set before du has completed, the process will be killed. Under Unix, this sends a signal (SIGKILL by default). Under Win32, TerminateProcess is called. So yeah, touch that variable and it's over for them πŸ’€


πŸ’œ VARIABLE πŸ’œ

Here's another example, this time using the -output option to direct output to a variable:

global myStatus myOutput
blt::bgexec myStatus -output myOutput du -s $dir
puts "Disk usage for $dir is $myOutput"

Upon completion, MyOutput will contain the output of the program. Chef's kiss 🫢


πŸ“ STDERR πŸ“

Various bgexec options can be used to capture stderr separately from stdout:

global myStatus myOutput myErrs
blt::bgexec myStatus -output myOutput -error myErrs du -s $dir

The -error option is similar to -output in that it sets a variable when the program completes with data written to stderr.


🧚 LOCAL 🧚

By default, bgexec treats variable or command options as being in the global namespace. The -local option can change this to use the current namespace. Thus data can be collected to namespace-local variables, even those inside of procs, eg:

proc Work {} {
  blt::bgexec myStatus -local 1 -output val -error err du -s
  puts "VAL=$val"
  puts "ERR=$err"
}

...which collects data to local variables.

For detached processes, -local will cause data to aggregate to namespace variables, ie. outside the proc, eg:

namespace eval ::Ns {
  set pval {}
  set perr {}
  proc Work {} {
    blt::bgexec myStatus -local 1 -output pval -error perr du -s &
  }
}

This collects data to ::Ns::pval and stderr to ::Ns::perr. Similarly, proc names (eg -onoutput) will be relative to the current namespace. Namespaces stay winning πŸ’…


⭐ PROGRESS ⭐

The -output and -error variables are set only after the program completes. But if a program runs for a long time, you can gather data as it becomes available using the -onoutput option. As new data becomes available, this command is executed, with data appended as an argument:

proc GetInfo { data } { puts $data }

blt::bgexec myStatus -onoutput GetInfo du -s $dir

The -onerror option performs a similar function for the stderr data stream. Live updates, real-time tea β˜•


πŸ”₯ ERROR HANDLING πŸ”₯

Like exec, bgexec returns an error if the exit code of the program is non-zero. To handle this, invoke bgexec from within a catch:

catch { blt::bgexec myStatus -output myOutput du -s $dir }

Detached jobs will generate an error only if the program startup failed. Otherwise the only indication is the status code set in myStatus.


πŸ’« TKWAIT πŸ’«

By default, bgexec waits for a program to finish and returns the resulting output. To detach a program, simply append an ampersand (&) as the last argument on the command line, eg:

global myStatus myOutput
blt::bgexec myStatus -output myOutput du -s $dir &

bgexec will then return immediately with the spawned process ids as the result. If needed, tkwait can be used to wait for the program to finish:

global myStatus myOutput
blt::bgexec myStatus -output myOutput du -s $dir &
      ...
tkwait variable myStatus
Note however that using tkwait can be dangerous. Multiple tkwait/vwait calls must complete in the reverse order called. The BLT busy command can be used to try and enforce this, but a better alternative is to just use -command instead. ⚠️

πŸ’« DIFFERENCES WITH EXEC πŸ’«

okay bestie so real talk, bgexec is not just exec with a glow-up, she actually moves different:

  • if you call bgexec without an ampersand, Tcl does not hang. like, the call blocks, yeah, but events keep getting serviced by the event handler the whole time. main character behavior fr. πŸ‘‘
  • and unlike exec, an error is not generated when output shows up on stderr. no dramatic meltdown, she's chill. 😌
  • plus, stderr output can be separately managed and collected, so you never have to do the whole "redirect it to a file" struggle. πŸ™…β€β™€οΈ
  • and finally, bgexec makes sure that invoked processes get properly cleaned up at termination. no messy leftovers, she cleans up after herself, we love to see it. ✨

πŸ¦‹ DIFFERENCES WITH FILEEVENT πŸ¦‹

so since Tk 4.0, you could pull off a subset of what bgexec does using the fileevent command. the steps for running a program in the background are literally this:

  1. execute the program with the open command (using the "|" syntax) and save the file handle. 🫢
global fileId
set fileId [open "|du -s $dir" r]
  1. 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
  }
}

but here's the tea 🍡 β€” bgexec is simpler and less error prone than using open + fileevent. you don't have to worry about non-blocking I/O. everything is handled for you automatically. it's giving "let her cook." πŸ’…

moreover, bgexec can run programs that fileevent can not. fileevent assumes that 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. 😭 and in the example above, we assume that the program will write and flush its output line-by-line. however when running another program, your application can block in the gets command reading a partial line. πŸ’€

bgexec gives you get back the exit status of the program. it also lets you reliably kill detached processes and allows you to collect data from both stdout and stderr individually. and finally, since data collection is handled in C code, bgexec is faster and more efficient. speed, accuracy, and a clean exit β€” she really does it all. 🌈⭐


🌸 SEE ALSO 🌸

  • busy
  • exec
  • tkwait
  • vwait

πŸŽ€ KEYWORDS πŸŽ€

exec, background, busy


and that's the whole page bestie, you made it to the end πŸ₯ΉπŸ«Ά this was BLT version 2.5, aka the blt::bgexec(3tcl) manual page. go forth and background-process responsibly, ily, bye! πŸ’–βœ¨πŸ¦‹

πŸ“¦ where this page comes from