Eu acho que você quer simplificar e otimizar isso o máximo possível. Agora não consigo pensar em nenhuma solução melhor do que ir passo a passo. Ou seja,
$ echo out1 >> log
$ echo err1 | tee -a log >&2
err1
$ echo out2 >> log
$ echo err2 | tee -a log >&2
err2
$ cat log
out1
err1
out2
err2
Bem, você pode usar um coprocess, para que você não precise chamar tee muitas vezes, se isso for importante para você.
$ coproc tee -a log
[1] 26417
$ echo out1 >> log
$ echo err1 >&"${COPROC[1]}"
$ echo out2 >> log
$ echo err2 >&"${COPROC[1]}"
$ cat log
out1
err1
out2
err2
Agora, sejam quais forem os erros, eles estão esperando no canal de saída do coprocess. Se você só precisa verificar se há alguma coisa lá, isso vai fazer:
$ timeout 1 head -n1 <&"${COPROC[0]}" >/dev/null && echo yes # or something else
yes
Se você quiser reutilizar os erros, convém modificar o coprocessamento para se adequar ao seu objetivo.
Você também pode definir o redirecionamento para scripts e binários. Contanto que eles não tenham redirecionamentos conectados, é claro. Assim você pode fazer isso,
$ some_executable 2>&"${COPROC[1]}" >>log
E a lógica permanece a mesma.