1

I am trying to implement a named-pipe communication solution between two processes in Bash.

The first process writes something to the named pipe:

send(){
    echo 'something' > $NAMEDPIPE
}

And the second script is supposed to read the named pipe like this:

while true;do
  if read line < $NAMEDPIPE;do
      someCommands
  fi
done

Note that the named pipe has been previously created using the traditional command

mkfifo $NAMEDPIPE

My problem is that the reader script is not always running so that if the writer script tries to write to the named-pipe it will stay blocked until a reader connects to the pipe.

I want to avoid this behavior, and a solution would be to trap a SIGPIPE signal. Indeed, according to man 7 signal is supposed to be sent when trying to write in a pipe with no reader. So I changed my red function by:

read(){
    trap 'echo "SIGPIPE received"' SIGPIPE
    echo 'something' > $NAMEDPIPE
}

But when I run the reader script, the script stays blocked, and "SIGPIPE received" does not get printed.

Am I mistaking on the signal mechanism or is there any better solution to my problem?

codeforester
  • 39,467
  • 16
  • 112
  • 140
Matt
  • 162
  • 2
  • 11
  • So it send `SIGPIPE` when write fails means no reader right?. So you can `trap` it in write code. – Jayesh Bhoi Jun 13 '14 at 09:26
  • I think you are right Jayesh: when there is no one for reading the pipe, the writer might receive a SIGPIPE signal. I tried to catch it with the trap line in the last piece of code, but nothing change. Hence I'm wondering if I use the correct mechanism for trapping signals, or if, finally, no SIGPIPE signal is send. – Matt Jun 13 '14 at 11:06

1 Answers1

0

Here's a fun code I just made. Perhaps you can refer to this:

#!/bin/bash

shopt -s extglob

NAMEDPIPE=/var/run/sr-pipe
RECEIVER_PID_FILE=/var/run/sr-receiver-pid

function sender_start {
    # Create named pipe.

    if [[ -e $NAMEDPIPE ]]; then
        echo "[Sender] Named pipe \"$NAMEDPIPE\" already exists."
    else
        echo "[Sender] Creating named pipe \"$NAMEDPIPE\"."
        mkfifo "$NAMEDPIPE" || {
            echo "Failed to create named pipe \"$NAMEDPIPE\"."
            exit 1
        }
    fi

    # Wait for receiver.

    echo "[Sender] Waiting for receiver."
    local PID
    until [[ -e $RECEIVER_PID_FILE ]] \
    && read PID < "$RECEIVER_PID_FILE" \
    && [[ $PID == +([[:digit:]]) ]] \
    && kill -s 0 "$PID" &>/dev/null; do
        sleep 1s
    done
    echo "[Sender] Receiver is [now] active."

    # Send signal.

    kill -s SIGPIPE "$PID"

    # Send messages.

    local SEND=''

    echo "[Sender] Now sending messages."
    while sleep 1s; do
        SEND=$RANDOM
        echo "[Sender] Sending $SEND."
        echo "$SEND" >&4
    done 4>"$NAMEDPIPE"
}

function receiver_start {
    echo "$BASHPID" > "$RECEIVER_PID_FILE"

    echo "[Receiver] Receiver is now active."

    local QUIT=false RECEIVE=false

    trap 'RECEIVE=true' SIGPIPE
    trap 'QUIT=true' SIGINT SIGTERM SIGHUP

    while [[ $QUIT == false ]]; do
        if [[ $RECEIVE == true ]]; then
            RECEIVE=false
            echo "[Receiver] Now receiving messages."
            while [[ $QUIT == false ]] && IFS= read -r -u 4 LINE; do
                echo "[Receiver] Received $LINE."
            done 4<"$NAMEDPIPE"
        fi
        sleep 1s
    done
}

if [[ $1 == send ]]; then
    sender_start
elif [[ $1 == receive ]]; then
    receiver_start
fi

On one terminal I ran this:

# bash sender-receiver.sh send
[Sender] Named pipe "/var/run/sr-pipe" already exists.
[Sender] Waiting for receiver.
[Sender] Receiver is [now] active.
[Sender] Now sending messages.
[Sender] Sending 21524.
[Sender] Sending 1460.
[Sender] Sending 8352.
[Sender] Sending 4424.
...

And on another I got this (corrected):

# bash sender-receiver.sh receive
[Receiver] Receiver is now active.
[Receiver] Now receiving messages.
[Receiver] Received 21524.
[Receiver] Received 1460.
[Receiver] Received 8352.
[Receiver] Received 4424.
...

You probably can run sender on background and receiver on the foreground on the same terminal.

konsolebox
  • 72,135
  • 12
  • 99
  • 105
  • Thank you konsolebox ! The solution I was looking for is included in the "until ... do ... done" part of your sender_start function. Still, I do not understand what is going wrong with the piece of code I provided in the question: why does the SIGPIPE signal is not send (or at least not trapped) while writing without reader? – Matt Jun 13 '14 at 12:06
  • You're welcome Matt. I suggest posting the whole code in one file (or of two files) so I could debug the problem you're really having. – konsolebox Jun 13 '14 at 12:12
  • Well, unfortunately this point is about a part of a project which already contains lots of code lines. I'd prefer to provide you with scripts that reproduce the problem I encounter. I can not write these scripts right now, but I could send it to you later. I really would like to understand the SIGPIPE mechanism. My only document for the moment is _man 7 signal_. I usually trust man pages, but on this point I feel like I need more better doc... – Matt Jun 13 '14 at 12:32
  • About your question about SIGPIPE not being caught, it actually depends on the part were your receiving script waits for it. If your script is "locked", the signal would be received but the trap won't be executed until your script moves again. – konsolebox Jun 13 '14 at 13:46