If I run
$#/bin/bash
for i in `seq 5`; do
exec 3> >(sed -e "s/^/$i: /"; echo "$i-")
echo foo >&3
echo bar >&3
exec 3>&-
done
then the result is not synchronous; it could be something like:
1: foo
1: bar
2: foo
2: bar
1-
3: foo
3: bar
2-
3-
4: foo
5: foo
4: bar
5: bar
4-
5-
How do I ensure that the process substitution >(...)
is completed before proceeding to the next iteration?
Inserting sleep 0.1
after exec 3>&-
helped, but it's inelegant, inefficient, and not guaranteed to always work.
EDIT: The example may look silly, but it was for illustration only. What I'm doing is reading a stream of input in a loop, feeding each line to a process which occasionally changes during the loop. Easier explained in code:
# again, simplified for illustration
while IFS= read line; do
case $line in
@*)
exec 3>&-
filename=${line:1}
echo "starting $filename"
exec 3> >(sort >"$filename"; echo "finished $filename")
;;
*)
echo "$line" >&3
;;
esac
done
exec 3>&-
Another user asks the same question, and receives an exhaustive answer here.
The following works in bash 4, using coprocesses:
For prior versions of bash, a named pipe can be used instead:
A few notes:
printf '%s\n' "$line"
thanecho "$line"
; if a line contains only-e
, for instance, some versions ofecho
will do nothing with it.Easy, just pipe everything into cat.
Here's the output:
The "obvious" answer is to get rid of the process substitution.
So the question becomes, do you really need to structure your code using process substitution? The above is much simpler than trying to synchronize an asynchronous construct.