None of the usual shells (even zsh) permit pipes with the |
operator other than from stdout to stdin. But all Bourne-style shells support file descriptor reassignment (as in 1>&2
). So you can temporarily divert stdout to fd 3 and stderr to stdout, and later put fd 3 back onto stdout.
If stuff
produces some output on stdout and some output on stderr, and you want to apply filter
on the error output leaving the standard output untouched, you can use { stuff 2>&1 1>&3 | filter 1>&2; } 3>&1
.
$ stuff () {
echo standard output
echo more output
echo standard error 1>&2
echo more error 1>&2
}
$ filter () {
grep a
}
$ { stuff 2>&1 1>&3 | filter 1>&2; } 3>&1
standard output
more output
standard error
Ksh, bash and zsh support pipes on arbitrary file descriptors, but they work differently: one command is the main command, and you can pipe its input or output into another command. Process substitution runs a command in the background with either its standard input or its standard output connected to a pipe which you can use in a redirection on the main command. Here you want to filter the standard error, so redirect it to an output process substitution, and redirect the output of the filter command back to stderr.
$ stuff () {
echo standard output
echo more output
echo standard error 1>&2
echo more error 1>&2
}
$ filter () {
grep a
}
$ stuff 2> >(filter >&2)
standard output
more output
standard error
Note that there are two >
characters with a space in between: that's 2>
to redirect standard output and >(…)
to make a process substitution. The space is necessary because 2>>…
would be parsed as an append redirection.
To make this approach function like the first part of the answer, redirect the output of the filter command back to stderr:
︙
$ stuff 2> >(filter >&2)