Domanda

I am using TCL-Expect and I'm executing a script within a script. Is it possible to shutdown the stderr stream and write all other messages to stdout?

    set runcmd [ exec $SCRIPTS_PATH/config $build_tag -u 2>&- >&stdout]

    # Just in case...
    if { [catch $runcmd res] } {
       send_user "Failed to run command due to: $res"
    }

The current behavior of the code above will not display anything to stdout, but stderrs are avoided and also not displayed.

È stato utile?

Soluzione

The Tcl (and expect) exec command defines its own redirections: see http://tcl.tk/man/tcl8.5/TclCmd/exec.htm -- you can't just assume sh/bash redirections will work.

You want

if { [catch {exec $SCRIPTS_PATH/config $build_tag -u 2>/dev/null} res] != 0 } {
   send_user "Failed to run command due to: $res"
}

If you want to send stderr to stdout, use 2>@1 (see the man page)

If you want to declare the command as a variable, do this:

set runcmd [list exec $SCRIPTS_PATH/config $build_tag -u 2>/dev/null]

if { [catch $runcmd res] != 0 } {
   send_user "Failed to run command due to: $res"
}

If you really want to use shell redirections, you have to execute the command in a shell:

set runcmd [list exec sh -c "$SCRIPTS_PATH/config $build_tag -u 2>&1"]

I don't see that 2>&- is valid in the bash man page: n<&- closes file descriptor n though.

Altri suggerimenti

To suppress stderr you can do:

exec config $build_tag -u 2>/dev/null

If I was running a command that wanted to spew irrelevant rubbish to stderr that I wanted to suppress, I'd ditch it with 2>/dev/null.

# The “list” is important! It says “build a list here” instead of direct evaluation
set runcmd [list $SCRIPTS_PATH/config $build_tag -u]
exec {*}$runcmd 2>/dev/null

That would still produce an error if $SCRIPTS_PATH/config has a non-zero exit code, but that's usually the right thing. To catch it:

if {[catch { exec {*}$runcmd 2>/dev/null }]} {
    # An error has been trapped
}

Tcl doesn't provide a way of running a subprocess with its stderr outright closed; that's a pretty strange state to be in really. Redirected to /dev/null is far more likely to be useful and sane.

On the other hand, if you were to want to run with the errors going to the outer Tcl script's stderr without causing an error if those messages are generated (a very irritating feature of exec at times) — so any error from exec only comes from the subprocess exit code — that's done with a different redirection, like this:

exec {*}$runcmd 2>@stderr

This is because exec normally traps the subprocess stderr to use as a source for better error messages. It's all made more complex by the fact that some commands use stderr to print error messages but don't actually error out correctly, yet others write logging information to stderr, and not just error messages. (It's all a bit of a swamp, and its the result of many people hacking together code over a long time. Tcl merely tries to do what it can in this space; I'm not convinced it gets it right, but past design choices are sanctified by the number of scripts that rely on them now.)


What I wouldn't do is put the exec or the redirection(s) in the list command invocation; I think that is too confusing. I prefer to keep “things that belong to Tcl” more directly expressed. This is just a matter of taste, but I think it is clearer. It also allows me to think of constructing the command and then firing it off to a subordinate shell for evaluation; the inner syntax is different (shell, not Tcl) but you can then do stuff like this:

set runcmd "$SCRIPTS_PATH/config $build_tag -u"
exec /bin/sh -c $runcmd 2>/dev/null

This is logically pretty equivalent, and better for some things.


If you're using 8.4 (or earlier!) still, you won't be able to use the {*} syntax. Provided $runcmd is constructed by list, you're fine doing this as an alternative.

eval exec $runcmd 2>/dev/null

Theoretically, you should actually write:

eval [list exec] [lrange $runcmd 0 end] [list 2>/dev/null]

But that's awful and the sort of thing that's only necessary when $runcmd might not be a canonical-format list. (We added the list-expansion syntax in 8.5 because we never wanted to see that sort of monstrosity again, and because we found that being sufficiently careful was hard in all cases, with being slapdash about it leading to bugs.)

If you're using 8.4 and aren't very strictly forced to stick with it, do upgrade to at least 8.5; 8.4 is no longer in support. (We did support it for well over a decade…) 8.5 is pretty strongly compatible with 8.4, but it's worth checking whether your code really works. It pays to be careful. If you hit a problem, ask a question on Stack Overflow about fixing it of course. For all that, my scripts all Just Worked when I migrated them so its worth a try.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top