我有一个程序可以读取和写入文件描述符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。这变得更加棘手,因为如果超出管道的内部缓冲区,可能会陷入死锁。
两个一般要点。
你不可能有两个读者;如果这样做,您会发现您的读取将不确定地交错,并且您的数据将在两个读取器之间共享。您不会将相同的数据复制到两个读取器。
您可以有两个(或更多)写入器,但您的输出数据将再次按照管道接收的顺序进行交错。除非您的编写者仔细同步,否则诸如
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