Tcl Output redirection from stdout to a file

3.2k Views Asked by At

I know this question has been asked several times here. I have looked at the responses, but couldn't figure out the way it worked.Can you please help me understand.

Here it goes:

I'm trying to source a tcl script on the tclsh command line, and I want to redirect the output of that script into a file.

$ source my_script.tcl

The script my_script.tcl is something like this:

set output_file final_result
set OUT [open $output_file w]

proc calculate{} { 
    <commands> 
    return $result
}

foreach value [calculate] { 
    puts $output_file "$value"
}

This script still throws out the output onto the stdout, while I expected it to redirect the output into a file specified as "final_result"

Can you please help me understand where I went wrong ?

2

There are 2 best solutions below

2
On

As you've described here, your program looks OK (apart from minor obvious issues). Your problem is that calculate must not write to stdout, but rather needs to return a value. Or list of values in your case, actually (since you're stuffing them through foreach).

Thus, if you're doing:

proc calculate {} {
    set result {}
    puts [expr {1 + 2 * 3}]
    return $result
}

Then you're going to get output written stdout and an empty final_result (since it's an empty list). If you change that to:

proc calculate {} {
    set result {}
    lappend result [expr {1 + 2 * 3}]
    return $result
}

then your code will do as expected. That is, from puts to lappend result. This is what I recommend you do.


You can capture “stdout” by overriding puts. This is a hack!

rename puts _puts
proc puts {args} {
    # Detect where we're running. IMPORTANT!
    if {[info level] > 1 && [lindex [info level -1] 0] eq "calculate"} {
        upvar 1 result r
        lappend r [lindex $args end]
    } else {
        _puts {*}$args
    }
    return
}

I'm not convinced that the code to detect whether to capture the value is what it ought to be, but it works in informal testing. (It's also possible to capture stdout itself by a few tricks, but the least horrible — a stacked “transformation” that intercepts the channel — takes a lot more code… and the other alternatives are worse in terms of subtleties.)

1
On

Assuming that calculate doesn't write to stdout, and all the other good stuff pointed out and suggested by @DonalFellows has been done...

You need to change the puts in the main script to

puts $OUT "$value"

The script as posted writes to a channel named final_result which almost certainly doesn't exist. I'd expect an error from the puts statement inside the foreach loop.

Don't forget to close the output file - either by exiting from the tclsh interpreter, or preferrably by executing

close $OUT

before you check for anything in it,