views:

71

answers:

2

Hi,

I found tcl exec command returns string from stdout first then stderr. For example, my following "test script" generates messages in this order:

puts "test started"
puts stderr "some non-fatal error goes to stderr"
puts "test passed"

Then I execute the script like this:

set ret [ catch { exec sh -c $cmd } msg ]

and what I get from $msg is:

test started
test passed
some non-fatal error goes to stderr

and this is really making me hard to get the correct result.

Can someone let if know if it is possible to get the messages from both stdout and stderr in order, and:

1) please do not redirecting like this, which can get them all in order indeed:

set ret [ catch {exec $cmd >&log.txt} msg ]

2) I have to call that tcl script in my tcl script, sorry

3) Nor can I source the .tcl test script directly because there are other scripts called between the two and it wont work if my tcl script just source that tcl script.

I am using tclsh 8.3

Not sure if this is asking too much. I hope someone may figure this out. Thanks.

A: 

Use source command instead of exec as you want to invoke a tcl script from within another tcl script:

set ret [catch {source $cmd} msg]
ardsrk
Thanks ardsrk. But sorry the call is like this: my tcl script calls -> a .sh shell file which sets system vars and calls -> the tcl test script. If i just source the .tcl script it won't work.
X.M.
I modified my question to make clear statement. I should have.
X.M.
+3  A: 

First off, let's define a simple command for the purposes of testing that we can be sure will test just what we need:

set cmd "echo a; echo b >&2; echo c"

Next, we use a little extra helper to handle the merging of the stdout and stderr streams (splitting the command over several lines for clarity only so that we can see where the catch wrapper is and where the wrapped exec is):

set ret [catch {
   exec sh -c $cmd |& cat
} msg]

If we test that, we'll find that we get $ret being 0, and $msg being the correctly ordered:

a
b
c

How does it work? The trick is |&, which does the merge when piping to another process. (We use cat because it just passes things through without interfering.)

If you're using Tcl 8.6 (in beta) you can use chan pipe to produce a channel that you can redirect stdout and stderr into with the 2>@ fileId form, but that's not so useful to you. (You are aware that 8.3 is rather obsolete? Even 8.4 is no longer really supported; 8.5 is what production-grade code is recommended to target.)

Donal Fellows
You'd have more options if you made a pipeline with `open`, but that's more complex to stitch together.
Donal Fellows
awesome!! cool!! though not very clear of the mechanism, i'm so glad to see your post and use this code. thank you!
X.M.
Donal Fellows