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
那么结果不同步;它可能是这样的:
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-
如何确保进程替换>(...)
在进行下一次迭代之前是否已完成?
插入sleep 0.1
after exec 3>&-
有帮助,但它不优雅、效率低下,并且不能保证始终有效。
编辑:这个例子可能看起来很傻,但这只是为了说明。我正在做的是在循环中读取输入流,将每一行输入到一个在循环期间偶尔会发生变化的进程。用代码更容易解释:
# 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>&-
以下工作在 bash 4 中,使用协进程:
#!/bin/bash
fd_re='^[0-9]+$'
cleanup_and_wait() {
if [[ ${COPROC[1]} =~ $fd_re ]] ; then
eval "exec ${COPROC[1]}<&-"
echo "waiting for $filename to finish" >&2
wait $COPROC_PID
fi
}
while IFS= read -r line; do
case $line in
@*)
cleanup_and_wait
filename=${line:1}
echo "starting $filename" >&2
coproc { sort >"$filename"; echo "Finished with $filename" >&2; }
;;
*)
printf '%s\n' "$line" >&${COPROC[1]}
;;
esac
done
cleanup_and_wait
对于 bash 的早期版本,可以使用命名管道来代替:
cleanup_and_wait() {
if [[ $child_pid ]] ; then
exec 4<&-
echo "waiting for $filename to finish" >&2
wait $child_pid
fi
}
# this is a bit racy; without a force option to mkfifo,
# however, the race is unavoidable
fifo_name=$(mktemp -u -t fifo.XXXXXX)
if ! mkfifo "$fifo_name" ; then
echo "Someone else may have created our temporary FIFO before we did!" >&2
echo "This can indicate an attempt to exploit a race condition as a" >&2
echo "security vulnarability and should always be tested for." >&2
exit 1
fi
# ensure that we clean up even on unexpected exits
trap 'rm -f "$fifo_name"' EXIT
while IFS= read -r line; do
case $line in
@*)
cleanup_and_wait
filename=${line:1}
echo "starting $filename" >&2
{ sort >"$filename"; echo "finished with $filename" >&2; } <"$fifo_name" &
child_pid=$!
exec 4>"$fifo_name"
;;
*)
printf '%s\n' "$line" >&4
;;
esac
done
cleanup_and_wait
一些注意事项:
- 使用起来更安全
printf '%s\n' "$line"
than echo "$line"
;如果一行仅包含-e
,例如,某些版本echo
不会用它做任何事。
- 使用 EXIT 陷阱进行清理可确保意外的 SIGTERM 或其他错误不会留下过时的 fifo。
- 如果您的平台提供了一种在单个原子操作中创建未知名称的 FIFO 的方法,请使用它;这将避免需要我们始终测试 mkfifo 是否成功的情况。
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)