Version 49 of catch

Updated 2015-08-31 14:02:55 by oehhar

COMMAND

catch - Evaluate script and trap exceptional returns

USAGE

catch script ?messageVarName? ?optionsVarName?

Synopsis

CONTEXT

TCL core command

DESCRIPTION

The catch command may be used to prevent errors from aborting command interpretation. Catch calls the Tcl interpreter recursively to execute script, and always returns without raising an error, regardless of any errors that might occur while executing script. catch script ?messageVarName? ?optionsVarName? If script raises an error, catch will return a non-zero integer value corresponding to one of the exceptional return codes (see tcl.h for the definitions of code values). If the messageVarName argument is given, then the variable it names is set to the error message from interpreting script. If optionsVarName is given, then the variable it names is set to a dictionary describing details of the exceptional situation.

If script does not raise an error, catch will return 0 (TCL_OK) and set the variable to the value returned from script.

Note that catch catches all exceptions, including those generated by break and continue, and return, as well as errors. The only errors that are not caught are syntax errors found when the script is compiled. This is because the catch command only catches errors during runtime. When the catch statement is compiled, the script is compiled as well and any syntax errors will generate a Tcl error.

MAN PAGE

http://www.tcl.tk/man/tcl8.5/TclCmd/catch.htm

SEE ALSO

break, continue, return, error, errorCode, errorInfo, try

Documentation

EXAMPLES

The catch command may be used in an if to branch based on the success of a script. if {catch {open $someFile w} fid} {

 if { [catch {open $someFile w} fid] } {
   puts stderr "Could not open $someFile for writing\n$fid"
   exit 1
 }

Catching large chunks of code

Why is it a bad idea to "catch" large chunks of code? Because Tcl stops execution of the code as soon as it encounters an error. This behavior can lead to problems like this:

    # I've got an open socket whose handle's stored in fid
    catch {
        puts $fid "Here's my last message."
        close $fid
    } err
If the "puts" command generates an error, "catch" detects that and stops execution of the code block. You never get around to closing the channel. It's better practice to put separate "catch" commands around
both the "puts" and the "close" commands to detect errors in either case and handle them appropriately.

This is a different style of programming than in a language like Java. In Java, you can have a variety of exceptions, each represented by a different class, that signal different types of error conditions. Then
in a "try" block, you can test for the different types of error conditions separately and handle them in different ways. (My complaint about Java is that there seems to be so many different classes, I'm often at a loss as to which ones I should be testing for.) In contrast, Tcl generally has only one type of error. In theory, we could use different return codes to signal different types of error, but in practice this is hardly ever used.

----

I think it is extreme parochialism to state "it's a bad idea to code this/that way".  It is perfectly reasonable to catch "large" chunks:
  if { [ catch {
    close $fid

} err ] } {

 } err ] } {
    return -code error $err

}

 }

If you catch "large" chunks you can at least have a program that can tolerate some errors that you did not anticipate. It is certainly better to fix problems the first time they appear, but tinkering with the code on a live system is rather poor practice.

Most users would rather not have the software come to a screeching halt on every unanticipated error. -PSE


re-throwing a catched error

set resource [some allocator]
if {[set result [catch {some code with $resource} resulttext]]} {
    # remember global error state, as de-allocation may overwrite it
    set einfo $::errorInfo
    set ecode $::errorCode

    # free the resource, ignore nested errors
    catch {deallocate $resource}

    # report the error with original details
    return -code $result \
           -errorcode $ecode \
           -errorinfo $einfo \
           $resulttext
}
deallocate $resource

continue normally

Besides a full-scale Java-style try implementation with all kinds of functionality for the catch branches, this could probably also be improved with a simpler command withresource, that doesn't have the catch branch functionality, like this: Besides a full-scale Java-style try implementation with all kinds of functionality for the catch branches, this could probably also be improved with a simpler command withresource, that doesn't have the catch branch functionality, like this:

set resource [some allocator]
withresource {
    some code with $resource
} finally {
    # this would be executed always, even in case of error
    deallocate $resource
}

continue normally

HaO 2012-02-17: Within the upper code, one may use the tcl 8.5 feature of an option dict returned by catch. HaO 2012-02-17 Within the upper code, one may use the tcl 8.5 feature of an option dict returned by catch. Here is the modified code which does the same:

set resource some allocator if {[set result [catch {some code with $resource} resulttext resultoptions]} {

    # free the resource, ignore nested errors

    catch {deallocate $resource}

    # report the error with original details
    dict unset resultoptions -level
    return -options $resultoptions $resulttext

} deallocate $resource

continue normally

2015-12-07: [heinrichmartin] proposed on clt to use `[dict incr] resultoptions -level` instead of `[dict unset] resultoptions -level`.  Results in the same but might be more straightforward.
----
***Old catch bug discussed***

[MS]: ''Oops, forgot to mention it here: this bug was closed on 2000-10-25, it is fixed in tcl8.4a4.''

Note that the bytecode compiling of [[catch]] in Tcl 8.x has a bug. '''[catch]''' will inappropriately catch errors in the substitution of its arguments:

 % # should return 'foo'
 % catch [error foo]
 1

Compare with Tcl 7.6:

 % catch [error foo]
 foo

To work around the bug, defeat the bytecode compiler by generating '''[catch]''':

 % [format catch] [error foo]
 foo

Track Sourceforge bug # 119184 to learn if/when this bug is fixed.
http://sourceforge.net/bugs/?func=detailbug&bug_id=119184&group_id=10894

'''DGP'''

----
***Catch return example***

 % proc foo {} {
        puts "catch result is :[catch { return}]"
        puts "after return"
 } 
 % foo
 catch result is :2
 after return 

Catching break, continue, error, and return

For a more complex worked example of catching [break], [continue], [error] and [return], this Wiki has a pure Tcl implementation of a Java-style try ... finally ... construct. -- KBK 24 Dec 2000

catch and exec


The options dictionary from catching a call to exec contains a wealth of When you [catch] the result of an [exec] command, $::errorCode contains a wealth of information about what happened. See the exec page for how to take it apart. On comp.lang.tcl, Ulrich Schoebel shows this as an example of how to get at the exit code of a command being exec'd in Tcl:


Early throw discussion

[The following discussion was held before Tcl 8.6 added try/throw.]

[MS]: this will work as long as the ''unknown'' proc has not been modified, and is relatively slow as the whole error processing machinery is also set in motion. If you want to use this approach in a more robust and fast manner, you may want to define
   proc throw {{msg {}} {code 10}} {
       return -code $code $msg
   }

This will throw an exception, caught by catch; it will only be an error if code is set to "error" (or 1); do not use codes 0 to 4 in general as they have special meaning:

   0       TCL_OK         normal return 
   1       TCL_ERROR      error return
   2       TCL_RETURN     cause the *caller* to return
   3       TCL_BREAK      call [break] in the caller
   4       TCL_CONTINUE   call [continue] in the caller
----
***catch and external commands ***

A common use for catch is when invoking external commands via [exec] . Since '''any''' non-zero return code would otherwise result in a raised error, one typically wraps the invocation with catch and then checks for the appropriate things.  Since grep is just one example of a Unix command which uses a non-zero return code to mean something other than ''error'', one needs to be aware of cases like this when writing the exec.

[[does someone have an idiom for execing a command, capturing the stdout, stderr, return code, etc. testing the return code and producing useful output based on stderr when appropriate, etc.?  Answer:
"[UNIX only exec wrapper]" is one model.]]
** `catch` and stderr **
----
***catch and stderr***
Someone mentioned on [comp.lang.tcl] that it took them a while to understand that when you use catch and supply a variable, the output from the command that would go to stderr ends up in the variable.
Someone mentioned on comp.lang.tcl that it took them a while to understand that when you use catch and supply a variable, the output from the command that would go to stderr ends up in the variable.

----
***catch and exec status***

On comp.lang.tcl, Ulrich Schoebel shows this as an example of how to get at the exit code of a command being [exec]'d in [Tcl]:
** `[return]` **
 if {[catch {exec grep -q pippo /etc/passwd} result]} {
  # non-zero exit status, get it:
  set status [lindex $errorCode 2]
 } else {
  # exit status was 0
  # result contains the result of your command
  set status 0
 }
[JMN] 2007-11-24:
[glennj]: the above is ''slightly'' lazy.  The definitive method is seen as KBK's contribution to the [exec] page.
I've been in the habit of using the idiom:
[LES]: or should one rather follow advice given at [exec and error information]?

----
[JMN] 2007-11-24
 if {[catch {
   #somescript
 } result_or_errormsg]} {
   #handle error
 } else {
   #normal processing
 }

However.. it's possible that a non-error situation in the script can give the return value of [catch] a value other than 0 for example, if you simply use a [return] to exit the script early. You could use the extra ?optionsVarName? argument to examine the specific -code value, but in most cases that's more complicated than necessary, and I was hoping to keep the overall 'if structure' more or less in place. I'm now leaning towards replacing the above idiom with the following:

 if {1 == [catch {
   #somescript
 } result_or_errormsg]} {
   #handle error
 } else {
   #normal processing
 }
This suffers from the problem that `return -code error` in the script will now not result in the error handling branch being run..
This suffers from the problem that 'return -code error' in the script will now not result in the error handling branch being run..
Anyone got a neater way?

I initially expected the return value of [[catch]] would align with $code if 'return -code $code' was used, but [[catch]] will always have the value 2 if [[return]] is used.
----
*** catch and background ***

Often a Tcl/Tk program invokes an external command , but needs the GUI to
stay alive. The recommendation frequently is to use
catch {exec somecommand &}

However, what would be a strategy if you wanted to catch the output and somecommand's return code? However, what would be a strategy if you wanted to catch the output and somecommand's return code?


See also