带子进程的双向管道

Posted

技术标签:

【中文标题】带子进程的双向管道【英文标题】:bidirectional pipe with children process 【发布时间】:2020-08-04 20:09:23 【问题描述】:

我想运行一个可以从/向父进程读取数据和向父进程写入数据的子进程,其方式是子进程使用标准 stdin/stdout,并且不使用命名管道。

我编写了简单的测试程序,这是它应该做的:

父母启动孩子,然后听它; 孩子向父母发送start; 父母向孩子发送一个介于 1 和 5 之间的随机数; 孩子读取这个号码,如果号码等于3,则发送yes给父母,否则发送no; 父母读取答案,如果收到yes则退出,否则向孩子发送另一个随机数; 将日志打印到文件中以便更好地理解。

parent.sh

#!/usr/bin/env bash
# Usage: parent.sh ./child.sh

INPUT="/tmp/input"
OUTPUT="/tmp/output"
LOG_FILE="./logger"

echo "[parent] Start" >> $LOG_FILE

rm -f $INPUT $OUTPUT
mkfifo $INPUT $OUTPUT

echo "[parent] Starting child" >> $LOG_FILE
$1 > $INPUT < $OUTPUT &

echo "[parent] Reading child output..." >> $LOG_FILE
while read answer; do
    echo "[parent] Received from child: $answer" >> $LOG_FILE
    if [ "$answer" = "yes" ]; then
        break
    else
        number=$(( ( RANDOM % 5 )  + 1 ))
        echo "[parent] Sending to child: $number" >> $LOG_FILE
        echo "$number" > $OUTPUT
    fi
done < "$INPUT"

echo "[parent] end" >> $LOG_FILE

child.sh

#!/usr/bin/env bash

LOG_FILE="./logger"
SECRET_NUMBER=3

echo "[child] Start" >> $LOG_FILE
echo "start"

while read line; do
    echo "[child] Received from parent: $line" >> $LOG_FILE
    if [ "$line" == "$SECRET_NUMBER" ]; then
        answer="yes"
    else
        answer="no"
    fi
    echo "[child] Sending to parent: $answer" >> $LOG_FILE
    echo $answer
done

echo "[child] End" >> $LOG_FILE

我以子脚本作为参数 (parent.sh ./child.sh) 执行父脚本,然后在另一个终端 (tail -f ./logger) 中打开记录器。

似乎永远不会执行孩子:

[parent] Start
[parent] Starting child
[parent] Reading child output...

为什么?

如果我将$1 &gt; $INPUT &lt; $OUTPUT &amp; 行替换为$1 &gt; $INPUT &amp;,脚本会执行,但不会进入主循环就结束。

编辑:修复了echo answer 错误

【问题讨论】:

你说你不想使用命名管道,但你正在使用mkfifo 将“回显答案”更改为echo $answer 另外,您可能希望将父级的重定向移出循环并写入:... done &lt; $INPUT &gt; $OUTPUT @Barmar 我不想在子脚本中使用命名管道,但在父脚本中可以。 @WilliamPursell 谢谢!这是公认的答案。 【参考方案1】:

有两个问题。一个是次要的:在孩子身上,你不小心写了echo answer 而不是echo $answer。另一个很重要。通过在父级中写入echo $number &gt; $OUTPUT,您将在循环的每次迭代中关闭并重新打开fifo。相反,您希望保持打开状态并将重定向移到循环之外。那就是:

while read answer; do
    echo "[parent] Received from child: $answer" >> $LOG_FILE
    if [ "$answer" = "yes" ]; then
        break
    else
        number=$(( ( RANDOM % 5 )  + 1 ))
        echo "[parent] Sending to child: $number" >> $LOG_FILE
        echo "$number"
    fi
done < "$INPUT" > $OUTPUT

【讨论】:

【参考方案2】:

我不能代表其他任何人,但如果我需要做这样的事情,我会运行一个本地 IRC 服务器并编写两个机器人(编程客户端;基本上使用脚本运行 XChat)代表父级和孩子。这将是我能想到的最简单的方法。

使用 IRC,您可以使用聊天频道或传入的私人消息作为事件触发器。因此,当孩子向他们所在的频道发送“开始”时,父母会立即检测到它。您可以使用这些触发器在 IRC 客户端本身之外执行代码。

【讨论】:

感谢您的建议。我想使用标准输入和标准输出,因为它们是简单的概念,并且子脚本必须尽可能易于访问,因为它们将由其他用户编写。

以上是关于带子进程的双向管道的主要内容,如果未能解决你的问题,请参考以下文章

子进程和线程的区别

管道子流程标准输出到变量[重复]

Perl 行为差异关闭由 open() 产生的子进程与 IPC::Open3

渲染管道子流程

渲染管道子流程

僵尸进程