Pipe only STDERR through a filter
Is there any way, in bash, to pipe STDERR through a filter before unifying it with STDOUT? That is, I want
STDOUT ────────────────┐
├─────> terminal/file/whatever
STDERR ── [ filter ] ──┘
rather than
STDOUT ────┐
├────[ filter ]───> terminal/file/whatever
STDERR ────┘
Here's an example, modeled after how to swap file descriptors in bash . The output of a.out is the following, without the 'STDXXX: ' prefix.
STDERR: stderr output
STDOUT: more regular
./a.out 3>&1 1>&2 2>&3 3>&- | sed 's/e/E/g'
more regular
stdErr output
Quoting from the above link:
- First save stdout as &3 (&1 is duped into 3)
- Next send stdout to stderr (&2 is duped into 1)
- Send stderr to &3 (stdout) (&3 is duped into 2)
- close &3 (&- is duped into 3)
TL;DR:
$ cmd 2> >(stderr-filter >&2)
Example:
% cat /non-existant 2> >(tr o X >&2)
cat: /nXn-existant: NX such file Xr directXry
%
This will work in both bash and zsh. Bash is pretty much ubiquitous these days, however, if you really do need a (really gnarly) solution for POSIX sh
, then see here.
Explanation
By far, the easiest way to do this is to redirect STDERR via process substitution:
Process substitution allows a process’s input or output to be referred to using a filename. It takes the form of
>(list)
The process list is run asynchronously, and its input or output appears as a filename.
So what you get with process substituion is a filename.
Just like you could do:
$ cmd 2> filename
you can do
$ cmd 2> >(filter >&2)
The >&2
redirect's filter
's STDOUT back to the original STDERR.
A naive use of process substitution seems to allow filtering of stderr
separately from stdout
:
:; ( echo out ; echo err >&2 ) 2> >( sed s/^/e:/ >&2 )
out
e:err
Note that stderr
comes out on stderr
and stdout
on stdout
, which we can see by wrapping the whole thing in another subshell and redirecting to files o
and e
( ( echo out ; echo err >&2 ) 2> >( sed s/^/e:/ >&2 ) ) 1>o 2>e