In my early days of Unix, it was explained to me that a pipe works just like plumbing.
A process basically has 3 connection points for your plumping:
stdin
|
+--+--+
| pgm |
++---++
| |
stdout stderr
So, when you do cat | grep pattern
, you get
stdin
|
+--+--+
| cat |
++---++
stdout| |stderr
| \
stdin| \
+------+ |
| grep | |
+-+---++ |
stdout| | |
| \__ |
| \|
| |stderr
(in those days, it was all ascii-graphics...)
When you redirect stderr to stdout, as in pgm 2>&1
, the picture becomes:
stdin |
|
+--+--+
| pgm |
++---++
stdout| |stderr
| /
| /
| /
|/
|
In theory, you can do a lot of plumbing this way. For example: awk '{print;print > "/dev/stderr"}'
copies stdin to both stdout and stderr.
However, it becomes quickly very confusing. See the bash manual for details.
NOTE: pgm = program in above diagrams.
EDIT:
For fun, I tried a bit of more elaborate plumbing using simple shellscripts.
generate.sh
:
echoerr() { echo "$@" 1>&2; }
for i in 1 2 3 4 5 6 ; do
echo "STDOUT $i"
echoerr "STDERR $i"
done
and copytee.sh
:
#!/bin/bash
echoerr() { echo "$@" 1>&2; }
while read line; do
echo "stdout $line"
echoerr "stderr $line"
done
(note that copytee ads lower-case and generate adds uppercase)
To get in advanced forms of redirection, bash generate.sh 2> >(bash copytee.sh )
gives as output:
STDOUT 1
STDOUT 2
STDOUT 3
STDOUT 4
STDOUT 5
STDOUT 6
stdout STDERR 1
stderr STDERR 1
stdout STDERR 2
stderr STDERR 2
stdout STDERR 3
stderr STDERR 3
stdout STDERR 4
stderr STDERR 4
stdout STDERR 5
stderr STDERR 5
stdout STDERR 6
stderr STDERR 6
which is exactly what you asked for.