I have a process that generates output both on stderr
and stdout
.
I need to pipe these two different commands, but I would also like to keep seeing them on the terminal.
So I tried something like this as a proof of concept:
#!/usr/bin/env bash
set -e
function generate_output() {
echo This message goes to stderr 1>&2
echo This message goes to stdout
}
generate_output \
1> >(tee <&0 >(cat > out.log)) \
2> >(tee <&0 >(cat > err.log))
cat > out.log
is a dummy command that will be replaced by something else when I figure out how to make this work.
It almost works :
$ cat err.log
This message goes to stderr
And I see the output on the terminal.
So far so good !
But :
$ cat out.log
This message goes to stdout
This message goes to stderr
Why does the "stderr" message ends up in out.log ?
What puzzles me even more is that if I remove the tee command, the log file contain the expected result (but then I loose terminal output)
#!/usr/bin/env bash
set -e
function generate_output() {
echo This message goes to stderr 1>&2
echo This message goes to stdout
}
generate_output \
1> >(cat > out.log) \
2> >(cat > err.log)
CodePudding user response:
Both tee
s are writing to stdout. That's fine for the first one but a problem for the second one since when the 2>
redirection is processed 1>
has already redirected stdout. That means that the second tee
isn't writing to the terminal, but to the first tee
process. Oops!
An elegant way to fix it is to have each of the tee
s write to the fd that they're reading from. Adding >&2
to the second one will fix the problem. And since I can tell you like parallel structure, you can add an explicit >&1
to the first one as well.
This will have the nice effect of preserving the separation of stdout and stderr in case there are any downstream consumers of your script's output.
generate_output \
1> >(tee <&0 >(cat > out.log) >&1) \
2> >(tee <&0 >(cat > err.log) >&2)
You can then eliminate some redundancies:
<&0
redirects stdin into stdin, which does nothing.>(cat >file)
is a complicated way of writingfile
. You can get the same effect just by passingout.log
anderr.log
directly totee
.
generate_output \
1> >(tee out.log >&1) \
2> >(tee err.log >&2)
CodePudding user response:
The errors are going into out.log
because tee
is inheriting the redirected stdout. So when it writes to its stdout, it goes to the first tee
process.
Change the order of your redirections to fix this:
generate_output \
2> >(tee <&0 >(cat > err.log)) \
1> >(tee <&0 >(cat > out.log))