在 Bash 中处理协进程的一种非常稳健的方法是使用...内置的coproc。
假设您希望在后台运行一个名为banana 的脚本或函数,在执行stuff 的同时捕获其所有输出,然后等待它完成。我会用这个来模拟:
banana() {
for i in {1..4}; do
echo "gorilla eats banana $i"
sleep 1
done
echo "gorilla says thank you for the delicious bananas"
}
stuff() {
echo "I'm doing this stuff"
sleep 1
echo "I'm doing that stuff"
sleep 1
echo "I'm done doing my stuff."
}
然后您将运行 banana 和 coproc,如下所示:
coproc bananafd { banana; }
这就像运行banana &,但具有以下附加功能:它在数组bananafd 中创建两个文件描述符(在索引0 处用于输出,在索引1 处用于输入)。您将使用 read 内置函数捕获 banana 的输出:
IFS= read -r -d '' -u "${bananafd[0]}" banana_output
试试看:
#!/bin/bash
banana() {
for i in {1..4}; do
echo "gorilla eats banana $i"
sleep 1
done
echo "gorilla says thank you for the delicious bananas"
}
stuff() {
echo "I'm doing this stuff"
sleep 1
echo "I'm doing that stuff"
sleep 1
echo "I'm done doing my stuff."
}
coproc bananafd { banana; }
stuff
IFS= read -r -d '' -u "${bananafd[0]}" banana_output
echo "$banana_output"
警告:您必须在banana 结束之前完成stuff!如果大猩猩比你快:
#!/bin/bash
banana() {
for i in {1..4}; do
echo "gorilla eats banana $i"
done
echo "gorilla says thank you for the delicious bananas"
}
stuff() {
echo "I'm doing this stuff"
sleep 1
echo "I'm doing that stuff"
sleep 1
echo "I'm done doing my stuff."
}
coproc bananafd { banana; }
stuff
IFS= read -r -d '' -u "${bananafd[0]}" banana_output
echo "$banana_output"
在这种情况下,您将收到如下错误:
./banana: line 22: read: : invalid file descriptor specification
您可以检查是否为时已晚(即,您是否花费了太长时间执行 stuff),因为在完成 coproc 之后,bash 会删除数组 bananafd 中的值,这就是为什么我们得到了之前的错误。
#!/bin/bash
banana() {
for i in {1..4}; do
echo "gorilla eats banana $i"
done
echo "gorilla says thank you for the delicious bananas"
}
stuff() {
echo "I'm doing this stuff"
sleep 1
echo "I'm doing that stuff"
sleep 1
echo "I'm done doing my stuff."
}
coproc bananafd { banana; }
stuff
if [[ -n ${bananafd[@]} ]]; then
IFS= read -r -d '' -u "${bananafd[0]}" banana_output
echo "$banana_output"
else
echo "oh no, I took too long doing my stuff..."
fi
最后,如果你真的不想错过大猩猩的任何动作,即使你的stuff 花费的时间太长,你也可以将banana 的文件描述符复制到另一个fd,例如3 ,做你的事情,然后从3阅读:
#!/bin/bash
banana() {
for i in {1..4}; do
echo "gorilla eats banana $i"
sleep 1
done
echo "gorilla says thank you for the delicious bananas"
}
stuff() {
echo "I'm doing this stuff"
sleep 1
echo "I'm doing that stuff"
sleep 1
echo "I'm done doing my stuff."
}
coproc bananafd { banana; }
# Copy file descriptor banana[0] to 3
exec 3>&${bananafd[0]}
stuff
IFS= read -d '' -u 3 output
echo "$output"
这会很好用!最后一个read 也将扮演wait 的角色,这样output 将包含banana 的完整输出。
太棒了:无需处理临时文件(bash 静默处理所有内容)和 100% 纯 bash!
希望这会有所帮助!