多个进程之间的通信

all*_*azz 13 bash pipe shell-script concurrency

我有一个 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
Run Code Online (Sandbox Code Playgroud)

phe*_*mer 26

您尝试完成的术语是多路复用

这可以在 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
Run Code Online (Sandbox Code Playgroud)

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)与该进程的标准输入的路径。因此,如果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
Run Code Online (Sandbox Code Playgroud)

我在哪里打字helloworld

  • @c4f4t0r 这不是打字错误 (3认同)
  • @c4f4t0r open suse 11 中的 bash 版本非常古老(3.2)。这个特性是在 bash 4.0 中实现的。 (2认同)