您要完成的工作是多路复用。
在bash中可以很容易地做到这一点,但是它确实需要一些更高级的bash功能。
我根据您的想法创建了一个脚本,我认为它可以完成您要完成的任务。我会在下面解释。
#!/bin/bash
manager() {
while IFS= read -r line; do
echo "manager[$1:$BASHPID]: $line"
done
}
fds=()
for (( i=0; i<5; i++ )); do
exec {fd}> >(manager $i)
fds+=( $fd )
done
while IFS= read -r line; do
echo "master: $line"
for fd in "${fds[@]}"; do
printf -- '%s\n' "$line" >&$fd
done
done
manager
是一个bash函数,它简单地从STDIN读取并将其标识符和行写入STDOUT。我们用$BASHPID
,而不是$$
作为$$
没有得到亚壳层更新(这是我们将使用来启动manager
。
fds
是一个数组,其中包含指向manager
产生的各种s 的STDIN管道的文件描述符。
然后,我们遍历并创建5个管理器流程。我使用for (( ))
语法而不是您做的方式,因为它更干净。这是特定于bash的,但是此脚本所做的一些操作都是特定于bash的,因此不妨一直进行下去。
接下来我们去exec {fd}> >(manager $i)
。这还会执行一些bash特定的操作。
首先是{fd}>
。这将在编号10或之后获取下一个可用的文件描述符,打开一个管道,并在管道的写侧分配该文件描述符,然后将文件描述符号分配给变量$fd
。
在>(manager $i)
启动manager $i
和基本替代>(manager $i)
与该进程的标准输入的路径。因此,如果manager
以PID 1234的形式启动,则>(manager $i)
可能会替换为/proc/1234/fd/0
(这取决于OS)。
因此,假设下一个可用文件描述符号为10,并且使用PID 1234启动管理器,该命令exec {fd}> >(manager $i)
基本上变为exec 10>/proc/1234/fd/0
,并且bash现在具有指向该管理器的STDIN的文件描述符。
然后,由于bash将文件描述符号放入中$fd
,我们将该描述符添加到数组中fds
以备后用。
其余的都非常简单。主机从STDIN读取一行,遍历中的所有文件描述符$fds
,并将该行发送到该文件描述符(printf ... >&$fd
)。
结果看起来像这样:
$ /tmp/test.sh
hello
master: hello
manager[0:8876]: hello
manager[1:8877]: hello
manager[4:8880]: hello
manager[2:8878]: hello
manager[3:8879]: hello
world
master: world
manager[0:8876]: world
manager[1:8877]: world
manager[3:8879]: world
manager[2:8878]: world
manager[4:8880]: world
我在哪里输入hello
和world
。