I am trying to add some information in front of every line output from a file, by:
Here is my test script:
#!/bin/bash
#
# Test proxying stdout and stderr
#
function proxy-stdouterr() {
local name="$1"
local handle=$2
while IFS='' read -r line
do
echo -e "[ ${name}: ${line} ]" >&${handle}
done
}
# Output some messages and attempt to parse them
(
echo "1: Normal message"
echo "2: Error" >&2
echo "3: Normal message"
echo "4: Error" >&2
) 2> >(proxy-stdouterr "stderr" 2) > >(proxy-stdouterr "stdout" 1)
This works fairly well but doesn't preserve the order in the terminal (Ubuntu 12.04).
When not proxying outputs:
1: Normal message
2: Error
3: Normal message
4: Error
However when proxying the order isn't kept. Worse this is not deterministic, most of the time it is:
[ stderr: 2: Error ]
[ stderr: 4: Error ]
[ stdout: 1: Normal message ]
[ stdout: 3: Normal message ]
But occasionally:
[ stderr: 2: Error ]
[ stdout: 1: Normal message ]
[ stderr: 4: Error ]
[ stdout: 3: Normal message ]
How can I fix this?
Thanks
As others noted, this is a synchronization problem with no obvious answer without knowing more about which parts you specifically control and want to change.
There are IMHO two main surfaces of attack:
stream buffering. Most C-derived programs do not buffer stderr,
buffer stdin and stdout by line if they're a terminal and buffer
them fully otherwise. This explains why stderr tends to always come
up first in your example. A cheap cop-out is to use /usr/bin/echo
instead of builtin echo
, so you'll get a flushed stream at the end
of every line (and a lot of delay for process spawning). It solves
your example 100% for what I've tried, so it could be enough for you
too. If you've got control on the command that generates the lines,
ensure both stdout and stderr are line-buffered.
process scheduling. You've got two processes competing to read from
both streams. Should there be data flowing on both at the time,
they're in a race against each other. You can mitigate that by
having a single process do that, but I'm not sure it's possible to
keep it efficient in native bash
(no programmable POSIX select
available to the end-user)
In any case, if there's queueing anywhere in the picture, you're screwed as far as keeping the order goes.