This is a follow-up to this question:
How do I get both STDOUT and STDERR to go to the terminal and a log file?
In short: I want to run a command and store both, STDOUT
and STDERR
in one log file (to keep time correlation) but I need STDERR
output still to be printed on STDERR
.
Motivation: there are tools which run a command and treat STDOUT
and STDERR
differently - e.g. printing STDERR
in a different color or print STDERR
when the command returns non-zero.
So I'd like to have a way to store all output in one log file but preserve the distinction between STDOUT
and STDERR
(as well as the return code).
log-output --file=command.log -c "make stuff-with-stderr"
From what I found in the above links answers there are at least two different approaches:
the_cmd 1> >(tee stdout.txt ) 2> >(tee stderr.txt >&2 )
will store STDOUT
and STDERR
in separate files, thus loosing time correlation. And unfortunately both STDOUT
and STDERR
will be printed on STDOUT
only.
script -e -B build.log -c "the_cmd"
will store both STDOUT
and STDERR
in one file, keeping time correlation but still prints both STDOUT
and STDERR
on on STDOUT
only.
So none of those approaches meets my requirements. Is there something else?
CodePudding user response:
Edit:
Using my approach and your command from the comment below (pip3 install -U pytest
) in a bash shell you get your desired outcome:
(((pip3 install -U pytest | tee -a log.txt) 3>&1 1>&2 2>&3 | tee -a log.txt) 3>&1 1>&2 2>&3)
The entries in log.txt
are always in the correct order (same as executing pip3 install -U pytest
in a terminal) and the output is printed to the terminal correctly (separate streams for stdout and stderr). I suspect a better solution exists, but this appears to be a 'workable' solution to the problem.
Original answer:
There's probably a less awkward solution than this, but you could tee stdout to log.txt, then swap stdout with stderr and tee the 'new' stdout (stderr) to log.txt, then swap stderr back with stdout, then print both streams to terminal (similar to the example here):
((({ echo "test_out"; echo "test_err" 1>&2; } | tee -a log.txt) 3>&1 1>&2 2>&3 | tee -a log.txt) 3>&1 1>&2 2>&3)
test_out
test_err
cat log.txt
test_out
test_err
# show stdout/stderr are still separate streams:
((({ echo "test_out"; echo "test_err" 1>&2; } | tee -a log.txt) 3>&1 1>&2 2>&3 | tee -a log.txt) 3>&1 1>&2 2>&3) 1>std.out 2>std.err
cat std.out
test_out
cat std.err
test_err