Search code examples
bashnamed-pipes

bidirectional pipe with children process


I would like to run a child process that can read and write data from/to the parent process, in a way that the child process uses standard stdin/stdout, and doesn't use named pipes.

I wrote simple programs for testing, and this is what it's supposed to do:

  • the parent starts the child, then listen to it;
  • the child sends start to the parent;
  • the parent sends a random number between 1 and 5 to the child;
  • the child reads this number, and sends yes to the parent if the number equals 3, or no otherwise;
  • the parent reads the answer, then exits if yes is received, or sends an other random number to the child otherwise;
  • logs are printed to a file for a better understanding.

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

I execute the parent script with the child script as a parameter (parent.sh ./child.sh), then I open the logger in another terminal (tail -f ./logger).

It seems that the child is never executed:

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

Why?

If I replace the line $1 > $INPUT < $OUTPUT & by $1 > $INPUT &, the script is executed, but ends without entering in the main loop.

edit: fixed the echo answer mistake


Solution

  • There are two issues. One is minor: in the child, you've accidentally written echo answer instead of echo $answer. The other is significant. By writing echo $number > $OUTPUT in the parent, you are closing and reopening the fifo on every iteration of the loop. Instead, you want to keep it open and move the redirection outside the loop. That is:

    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