我有一个 bash 脚本,它将 manager() 函数作为单独的进程运行 x 次。如何从脚本内将消息转发到所有 manager() 进程?
我读过有关匿名管道的内容,但我不知道如何与其共享消息。我尝试使用命名管道进行操作,但似乎我必须为每个进程创建一个单独的命名管道?
最优雅的方法是什么?
到目前为止,这是我的代码:
#!/bin/bash
manager () {
while :
do
echo "read what has been passed to \$line"
done
}
x=1
while [ $x -le 5 ]
do
manager x &
x=$(( $x + 1 ))
done
while :
do
while read line
do
echo "What has been passed through the pipe is ${line}"
# should pass $line to every manager process
done < $1
done
exit 0
答案1
您想要完成的任务的术语是复用。
这可以在 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
而不是$$
as$$
不会更新子 shell(这是我们将用来启动manager
.
fds
manager
是一个数组,它将保存指向生成的各种 s 的 STDIN 管道的文件描述符。
然后我们循环并创建 5 个管理器进程。我使用for (( ))
语法而不是你这样做的方式,因为它更干净。这是特定于 bash 的,但该脚本所做的一些事情是特定于 bash 的,所以不妨一路走下去。
接下来我们到了exec {fd}> >(manager $i)
。这会执行更多 bash 特定的操作。
其中第一个是{fd}>
。这会获取编号 10 或之后的下一个可用文件描述符,打开一个管道,并将管道的写入端分配给该文件描述符,并将文件描述符编号分配给变量$fd
。
启动>(manager $i)
并manager $i
基本上替换>(manager $i)
为该进程的 STDIN 路径。因此,如果manager
作为 PID 1234 启动,>(manager $i)
可能会被替换为/proc/1234/fd/0
(这取决于操作系统)。
因此,假设下一个可用的文件描述符编号为 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
。
答案2
tee
和bash
:
cat foo | tee >(manager) >(manager) >(manager) >(manager) >(manager) >/dev/null
如果管理器的数量应该是可配置的,或者如果您希望不同管理器的输出不混合:
export -f manager
cat foo | parallel --pipe --tee manager ::: {1..10}