This question resembles Pipe only STDERR through a filter, but I want both STDOUT and STDERR and I do not want output merged.
I want:
STDOUT ── [ filter ] ────> STDOUT
STDERR ── [ filter ] ────> STDERR
And I want to keep $?. If myprogram is a function, I want it to be able to change the environment.
In Bash I can do:
myprogram() {
echo stdout;
echo stderr >&2;
myvar=this_value
(exit 3)
}
myfilter() {
perl -pe "s/^/$@ /"
echo filter "$@" stderr >&2
}
{ myprogram || foo=$?.$myvar; } 2> >(myfilter errfilter >&2) > >(myfilter outfilter)
wait
echo error=$foo
echo Done
This works on a read-only file system.
I want to do the same in /bin/sh. But there is no >(cmd) construct.
In /bin/sh I can do:
{ myprogram || foo=$?.$myvar; } >/tmp/tmpout 2>/tmp/tmperr
myfilter errfilter </tmp/tmperr >&2
myfilter outfilter </tmp/tmpout >&1
echo error=$foo
echo Done
(This will fail if stdout/stderr from myprogram is bigger than the disk, and will not work on a read-only file system).
Or:
mkfifo /tmp/fifoout
mkfifo /tmp/fifoerr
myfilter errfilter </tmp/fifoerr >&2 &
myfilter outfilter </tmp/fifoout >&1 &
{ myprogram || foo=$?.$myvar; } >/tmp/fifoout 2>/tmp/fifoerr
wait
echo error=$foo
echo Done
(This will fail if /tmp is read-only).
Can it be done in /bin/sh without using temporary files or named pipes?
I am thinking whether this can be done with some exec 2>&- 3>&1 magic.
This does not work, but I have the feeling it could work given the correct exec magic:
exec 3>&1 4>&2
myfilter outfilter <&3 >&1 &
myfilter errfilter <&4 >&2 &
{ myprogram || foo=$?.$myvar; } 1>&3 2>&4
wait
echo error=$foo
echo Done
Not tested in detail, but it seems to work:
However, it does not keep $? from
myprogram.