'''[https://tcl.tk/man/tcl/TclCmd/catch.htm%|%catch]''', a [Tcl Commands%|%built-in] Tcl command, evaluates a script and returns the [return%|%return code] for that evaluation. ** Synopsis ** '''`catch`''' ''`script`'' ?''`messageVarName`''? ?''`optionsVarName`''? ** Documentation ** http://www.tcl.tk/man/tcl/TclCmd/catch.htm: Official reference. [TIP] [http://www.tcl.tk/cgi-bin/tct/tip/90.html%|%90], Enable `[return] -code` in Control Structure Procs: Includes a good description of the operation of `catch`. ** See Also ** `[break]`: `[continue]`: `[return]`: `[error]`: [magic names]: [errorCode]: [errorInfo]: [Tcl performance: catch vs. info]: On the slowness of catch. `[try]`: [try ... finally ...]: [kbk] 2000-12-14: A [pure-Tcl] implementation of a [Java]-style `[try]` construct, along with a worked example of catching `[break]`, `[continue]`, `[error]`, `[return]`. [ucatch]: Deunicodifies the error message. ** Description ** '''`[catch]`''' is used to intercept the return code from the evaluation of ''script'', which otherwise would be used by the interpreter to decide how to proceed: Whether an error occurred, whether to break out of the current loop, whether to jump to the beginning of the loop and enter it again, whether the caller itself should return, etc. The caller of `[catch]` may then use this information, including values such as '''`-code`''' and '''`-level`''' in `$optionsVarname`, for its own purposes. The most common use case is probably just to ignore any error that occurred during the evaluation of `$script`. `$messageVarName` contains the value that result from the evaluation of `$script`. When an exceptional return code is returned, `$messageVarName` contains the message corresponding to that exception. The standard return codes are 0 to 4, as defined for `[return]`, and also in `tcl.h`. ** Examples ** Used with `[if]` as the condition: ====== if {[catch {open $someFile w} fid]} { puts stderr "Could not open $someFile for writing\n$fid" exit 1 } ====== ** Catching Large Chunks of Code ** [Ken Jones] once posted in [comp.lang.tcl]... 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 { puts $fid "Here's my last message." close $fid } err ] } { catch { close $fid } 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 Caught Error error ** [BR] - The code above reminds me of the biggest gripe I have with catch: Manually writing correct code for resource deallocation and re-throwing an error is somewhat tedious and error-prone: ====== 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: ====== 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. This allows relatively easy to return all error parameters to a caller. 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. ** Example: Catch `[return]` ** Example for caught return, from a posting of [George Petasis] in [comp.lang.tcl]: ====== % proc foo {} { puts "catch result is :[catch { return}]" puts "after return" } % foo catch result is :2 after return ====== ** `[catch]` and `[exec]` ** The options dictionary from catching a call to `[exec]` contains a wealth of information about what happened. See the `[exec]` page for how to take it apart. Also see [UNIX only exec wrapper]. 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]: ====== 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 } ====== [glennj]: the above is ''slightly'' lazy. The definitive method is seen as KBK's contribution to the [exec] page. [LES]: or should one rather follow advice given at [exec and error information]? ** Early `throw` Discussion ** The following discussion was held '''before''' Tcl [Changes in Tcl/Tk 8.6%|%8.6] added `[try]`/`[throw]`: '''throw''': The logical opposite to catching is throwing. Tcl has no ''throw'' command, but still you can call it. And guess what, it ends up in the hands of ''catch''.. see [Tricky catch], ''proc quotient_rep''. Now is this the Zen of Tcl, or what? [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: ======none 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 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. ** `[return]` ** [JMN] 2007-11-24: I've been in the habit of using the idiom: ====== 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.. 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? [HaO]: Use `[open]` and a command pipe, see the "|" character there. <> Arts and Crafts of Tcl-Tk Programming | Command | Control Structure