2

I have a program which reads from and writes to file descriptor 3. I want to let it write to fd 3, and be able to write to the other end of the pipe interactively which the program should read over the same fd. I can create a pipe with mkfifo (mkfifo /tmp/my_pipe) and remap one end of it to fd 3 (./prog &3>/tmp/my_pipe). I keep the pipe open using cat /tmp/my_pipe. However, when the process tries to read from fd 3, it receives a SIGTTIN. Is it possible to launch the program as I intend from bash?

3

On (at least) Linux-based systems a FIFO or Named Pipe is unidirectional. In your situation you want to have a program reading and writing to the same FIFO. This gets even more tricky because you may well up with a deadlock if you exceed the pipe's internal buffer.

Two general points.

  1. You cannot usefully have two readers; if you do, you'll find that your reads will interleave nondeterministically and your data will be shared between the two readers. You will not get the same data duplicated to both readers.

  2. You can have two (or more) writers, but once again your output data will be interleaved in the order that it is received by the pipe. Unless your writers are carefully synchronised, data such as First writer and Second writer could end up being received as the garbled FirSecond wrstwrititerer.

Using the program at the bottom of this answer, consider running the following two scenarios from different terminal sessions. Then try your own variations.

First terminal                Second terminal               Third terminal
--------------------          --------------------          --------------------
./fifo.sh read                ./fifo.sh read                ./fifo.sh write /etc/hosts

First terminal                Second terminal               Third terminal
--------------------          --------------------          --------------------
./fifo.sh write /etc/passwd   ps -ef | ./fifo.sh write      ./fifo.sh read

First terminal                Second terminal               Third terminal
--------------------          --------------------          --------------------
./fifo.sh both /etc/passwd

Script for fifo.sh as follows

#!/bin/bash
#
pipe=/tmp/mypipe


########################################################################
# Reader
#
doRead()
{
    echo "Reading from pipe $pipe" >&2
    nl <"$pipe"
}


########################################################################
# Writer. We pause after every line (for effect)
#
doWrite()
{
    [[ $# -eq 0 ]] && set -- -
    echo "Writing to pipe $pipe" >&2
    cat "$@" | while IFS= read -r line; do printf "%s\n" "$line"; sleep 1; done >>"$pipe"
}


########################################################################
# Reader-Writer. We pause after every line (for effect)
#
doBoth()
{
    [[ $# -eq 0 ]] && set -- -
    echo "Reading and writing to pipe $pipe" >&2
    exec 3<>"$pipe"
    nl <&3 & readPID=$!
    cat "$@" | while IFS= read -r line; do printf "%s\n" "$line"; sleep 1; done >&3
    kill $readPID 2>/dev/null
}


########################################################################
#
action="$1"
shift

if [[ ! -p "$pipe" ]]
then
    echo "Creating pipe $pipe" >&2
    mkfifo "$pipe"
fi


case "$action" in
    read*)      doRead "$@" ;;
    write*)     doWrite "$@" ;;
    both)       doBoth "$@" ;;
esac

Your Answer

By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.

Not the answer you're looking for? Browse other questions tagged or ask your own question.