I'm not a Tcl programmer, but I need to modify a Tcl script that invokes an external command and tries to separate stdout
and stderr
. The following is a minimal example of how the script currently does this.
#!/usr/bin/tclsh8.4
set pipe [open "|cmd" r]
while {[gets $pipe line] >= 0} {puts $line}
catch "close $pipe" errorMsg
puts "$errorMsg"
Here, cmd
is a an external command, and for the sake of this example, I will replace it with the following shell script. (I'm working on a Linux machine, but you can modify this to write to stdout
and stderr
however is appropriate for your system.)
#!/bin/sh -f
echo "A" > /dev/stdout
echo "B" > /dev/stdout
echo "C" > /dev/stderr
echo "D" > /dev/stderr
When I execute cmd
, I get the following four lines as expected:
% ./cmd
A
B
C
D
However, when I execute my Tcl script, I get:
% ./test.tcl
A
B
D
This is an example of a more general phenomenon, which is that catch
seems to swallow all but the last line of stderr
.
To me, the "obvious" way to approach this is to try to mimic what is happening with stdout
, which obviously works and prints all lines of the output. However, the current implementation is based on getting a Tcl channel by using open "|cmd"
, which requires running an external command. I can't figure out how to create a channel without opening an external command, and even if I could figure that out, there are subsequent issues with this approach. (How do I get the output of close
into the channel? And if I need to open a new channel to get the output of each channel I am closing, then wouldn't I need an infinite number of channels?)
If anyone has any idea why errorMsg
drops the initial lines or another approach that does not suffer from this problem, please let me know.
I know that this will come up, so I will say in advance that switching to Tcl 8.5 is probably not an option for me in the short term, since I do not control the environment in which this script is run.