2

In my main tcl script, I am calling a tcl proc wrapped in a catch command. This proc in-turn calls 10 more procs.

When there is an error in execution in any of those 10 procs, TCL still continues execution of my main script as expected and I am just able to view the error message which I captured. This error message may/may-not be conclusive enough to determine which of the 10 procs errored out during execution.

Is there a way to still keep capturing all the stdout until the point of error? I know it can be done by writing all messages (puts statements) in those 10 procs to another log file. But I'm interested in knowing if there is any other way.

2 Answers2

2

The catch command doesn't intercept I/O at all. To intercept output, the simplest and most method is to put a channel transform on that channel with chan push.

oo::class create Capture {
    variable contents encoding
    # Implement the channel interception protocol
    method initialize {handle mode} {
        set contents {}
        return {initialize finalize write}
    }   
    method finalize handle {
        # We do nothing here
    }
    method write {handle buffer} {
        append contents $buffer
        return $buffer
    }

    # Methods for ordinary people!
    method capture {channel body} {
        set encoding [chan configure $channel -encoding]
        chan push $channel [self]
        try {
            uplevel 1 $body
        } finally {
            chan pop $channel
        }
    }
    method contents {} {
        # Careful; need the encoding as channels work with binary data
        return [encoding convertfrom $encoding $contents]
    }
}

How to use this class:

set capt [Capture new]
$capt capture stdout {
    puts "Hello world!"
}
puts "Captured [string length [$capt contents]] characters"
puts [lmap c [split [$capt contents] ""] {scan $c "%c"}]

Output (I assume you recognise ASCII codes; the 13 10 at the end is a carriage-return/new-line sequence):

Hello world!
Captured 14 characters
72 101 108 108 111 32 119 111 114 108 100 33 13 10
Donal Fellows
  • 133,037
  • 18
  • 149
  • 215
  • There are other ways to do the interception, often by replacing `puts`, but they're a bit difficult to get right when the code is also doing file I/O. Channel transforms are far more reliable in my experience. – Donal Fellows Oct 27 '20 at 08:05
0

catch won't capture the stdout of a Tcl procedure, it will capture the return value.

A demo in an interactive tclsh:

% proc p {} {puts "some stdout"; return "return value"}
% catch p result
some stdout
0
% set result
return value
glenn jackman
  • 238,783
  • 38
  • 220
  • 352