GNU bash, version 5.1.8(1)-release (x86_64-pc-linux-gnu) I have attempted to use process substitution in order to feed the output of a command into two filters, one for handling stdout and the other for stderr.
Prior to this I was using POSIX sh and named pipes to achieve this but decided to try bash with its >() and <() notation. Perhaps I am misunderstanding how this works because I found them to be unusable in this context. What appears to be happening is that the output from standard error is being mixed into the function handling standard out, even more surprisingly that xtrace output is also being consumed and filtered as well. I don't quite know how to describe what's happening so I have provided a small demonstration/reproducer instead: #!/bin/bash -- generate() { for ((i=0; i<10; ++i)); do if ((RANDOM % 2)); then printf 'generate to stdout\n' else printf 'generate to stderr\n' >&2 fi done } stdout() { local line while read -r line; do printf 'from stdout: %s\n' "$line" done } stderr() { local line while read -r line; do printf 'from stderr: %s\n' "$line" done } # Using process substitution. unexpected() { # This is particularly dangerous when the script is executed under `bash -x' # as the xtrace output is read as standard input to the `stdout' function. generate > >(stdout) 2> >(stderr) wait # Example output: # from stdout: generate to stdout # from stdout: generate to stdout # from stdout: from stderr: generate to stderr # from stdout: from stderr: generate to stderr # from stdout: from stderr: generate to stderr # from stdout: generate to stdout # from stdout: from stderr: generate to stderr # from stdout: generate to stdout # from stdout: from stderr: generate to stderr # from stdout: from stderr: generate to stderr } # Using named pipes. expected() { mkfifo a b trap 'rm a b' EXIT generate > a 2> b & stdout < a & stderr < b & wait # Example output: # from stdout: generate to stdout # from stderr: generate to stderr # from stdout: generate to stdout # from stderr: generate to stderr # from stdout: generate to stdout # from stdout: generate to stdout # from stderr: generate to stderr # from stdout: generate to stdout # from stdout: generate to stdout # from stderr: generate to stderr }