Each file descriptor
can only point to a single file. File descriptors can
be duplicated such that both point to a single file, but a file descriptor
cannot point to two files.
Hence, you have to decide whether stdout points to out.txt or to both.txt,
and similarly with stderr.
Of course, you could use
to replicate output, since it writes its input directly to files (given as
arguments) and to stdout, which you can further redirect.
One such approach, proposed in "How to redirect
stderr and stdout to different files and also display in
((cmd | tee out.txt) 3>&1 1>&2 2>&3 | tee err.txt) &> both.txt
It, however, fails to preserve the order of
stdout and stderr output when writing to both.txt.
This is because once you send
cmd's stdout and stderr to different roads,
you can't guarantee that they will take the same time to traverse it.
More technically: The two Tees are different, concurrent processes, and as
such incur in a
as a result the order of the lines in both.txt is not deterministic.
Your proposed trick with Zsh
MULTIOS also fails to address this issue,
since, as documented,
it does essentially the same thing:
the shell opens the file descriptor as a pipe to a process that copies
its input to all the specified outputs, similar to tee, provided the MULTIOS
option is set, as it is by default.
Unfortunately, although piping both stdout and stderr to another program
preserves the line order for both.txt, the program then couldn't tell them
apart in order to write out.txt and err.txt. I.e., in
cmd 2>&1 | filter
filter only has a notion of its stdin; the information of whether this or that
line "came from"
cmd's stdout or stderr is completely lost.
The only reliable and feasible solution I see is to edit the program's source
code so as to insert information in its output that shows which lines are
stdout and which are stderr (e.g. by prepending "E:" to stderr and "O:"
to stdout). Too bad it is cumbersome and not scalable.