下面的测试脚本1的目的是启动一个“外部”协同进程(正在运行),在 -loop 中从此协同进程中读取,并且对于读取的每一行,打印一行标识外部循环的当前迭代,启动一个“ inner" 协进程(也在运行,带有新参数),在嵌套的 while 循环中从这个内部协进程中读取,然后清理这个内部协进程。嵌套的 while 循环为它从内部协进程读取的每一行打印一些输出。seq 3
while
seq
#!/bin/bash
# filename: coproctest.sh
PATH=/bin:/usr/bin
coproc OUTER { seq 3; }
SAVED_OUTER_PID="${OUTER_PID}"
exec {OUTER_READER}<&"${OUTER[0]}"
while IFS= read -r -u "${OUTER_READER}" OUTER_INDEX; do
printf -- '%d\n' "${OUTER_INDEX}"
START=$(( OUTER_INDEX * 1000000 ))
FINISH=$(( START + OUTER_INDEX ))
# (
coproc INNER { seq "${START}" "${FINISH}"; }
SAVED_INNER_PID="${INNER_PID}"
exec {INNER_READER}<&"{INNER[0]}"
while IFS= read -r -u "${INNER_READER}" INNER_INDEX; do
printf -- ' %d\n' "${INNER_INDEX}"
done
exec {INNER_READER}<&-
wait "${SAVED_INNER_PID}"
# )
done
exec {OUTER_READER}<&-
wait "${SAVED_OUTER_PID}"
当我运行这个脚本时,这是我得到的输出:
% ./coproctest.sh
1
./coproctest.sh: line 30: warning: execute_coproc: coproc [12523:OUTER] still exists
./coproctest.sh: line 19: INNER_READER: ambiguous redirect
./coproctest.sh: line 21: read: : invalid file descriptor specification
./coproctest.sh: line 25: INNER_READER: ambiguous redirect
2
./coproctest.sh: line 19: INNER_READER: ambiguous redirect
./coproctest.sh: line 21: read: : invalid file descriptor specification
./coproctest.sh: line 25: INNER_READER: ambiguous redirect
3
./coproctest.sh: line 19: INNER_READER: ambiguous redirect
./coproctest.sh: line 21: read: : invalid file descriptor specification
./coproctest.sh: line 25: INNER_READER: ambiguous redirect
如果我取消注释两条注释行,我会得到几乎相同的输出。
Q1:是否可以同时运行多个协同进程?
Q2:如果是这样,应该如何修改上面的脚本以实现所需的输出?
1我最近才开始使用协同进程,还有很多我不明白的地方。因此,该脚本几乎肯定包含不正确、笨拙或不必要的代码。请随时评论和/或修复您的回复中的这些弱点。
bash
从手册末尾的“BUGS”部分:正如@Kusalananda 所指出的,在 Bash v4 及更高版本(包括当前的 v5)上,正式没有。
但是,我可以告诉你,尽管有警告,它可能会起作用,但当然不能保证和 YMMV。请参阅此处了解更多信息。
一旦您修复了以下问题,它可能(如上所述)工作得很好:
这导致:
消息,因此还有:
信息。
它对我有用,一旦解决了这个问题并把警告放在一边。
至于其他注意事项:
coproc
的文件描述符,除非您想将它们传递给子进程(子外壳或命令或脚本)seq
命令自然很快完成,因此自动变量在您使用它们之前就消失了。通过这样做,那些重复的文件描述符将被所有后续命令和后台进程继承,如果您的协同进程实际使用其输入管道并等待它关闭以退出,这可能是不可取的。因此,另一种解决此问题的方法是通过同步机制,例如让您的OUTER
协同进程成为{ seq 3; exec >&-; read; }
,然后当您从主脚本消耗其输入时,例如echo >&${OUTER[1]}; wait "${OUTER_PID}"
让协同进程read
继续进行wait
。请注意,不能保证wait
将在$OUTER_PID
变量消失:在这种情况下,您可以将警告消息静音(或完全忽略它),并可能使用|| true
coproc
在后台进程和命名 FIFO 上使用 Bash v3 语法mkfifo
,在 Linux 上也是使用 Process Substitutions 而不是mkfifo
s 的技巧。使用 Bash v4 语法,它可以不那么复杂,但仍然是一个具有挑战性的练习。