如何在 bash 中创建双向管道?

如何在 bash 中创建双向管道?

我有一个程序可以读取和写入文件描述符3。我想让它写入 fd 3,并且能够以交互方式写入管道的另一端,程序应该读取同一个 fd 。我可以使用mkfifo( )创建一个管道mkfifo /tmp/my_pipe并将其一端重新映射到 fd 3( ./prog &3>/tmp/my_pipe)。我使用 保持管道打开cat /tmp/my_pipe。但是,当进程尝试读取 fd 时3,它会收到一个SIGTTIN.是否可以按照我的意愿从 bash 启动该程序?

答案1

在(至少)基于 Linux 的系统上,FIFO 或命名管道是单向的。在您的情况下,您希望有一个程序读取和写入同一个 FIFO。这变得更加棘手,因为如果超出管道的内部缓冲区,可能会陷入死锁。

两个一般要点。

  1. 你不可能有两个读者;如果这样做,您会发现您的读取将不确定地交错,并且您的数据将在两个读取器之间共享。您不会将相同的数据复制到两个读取器。

  2. 您可以有两个(或更多)写入器,但您的输出数据将再次按照管道接收的顺序进行交错。除非您的编写者仔细同步,否则诸如First writer和 之类的数据Second writer最终可能会被视为乱码FirSecond wrstwrititerer

使用本答案底部的程序,考虑从不同的终端会话运行以下两个场景。然后尝试你自己的变体。

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

脚本fifo.sh如下

#!/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

相关内容