命名管道在脚本中过早关闭?

时间:2011-07-26 17:55:49

标签: bash logging pipe named-pipes

LS:

prwx------ 1 root root 0 fifo

write.sh:

#! /bin/bash
while true;
do
    echo "blah" > fifo
done

read.sh:

#! /bin/bash
while true;
do
    cat fifo
done

我打开了两个终端,一个正在运行write.sh,另一个正在运行read.sh。当我首先启动write.sh时,它会挂起(就像它应该的那样)。然后我转到另一个终端并开始read.sh,它打印出"blah"很多次,然后write.sh停止。为什么我的写脚本会停止?这是一个小小的测试,我正在尝试更好地理解管道,因为我将把所有日志发送到管道,所以我可以在将它们写入文件之前解析它们。

我在这里缺少什么?

2 个答案:

答案 0 :(得分:5)

这里有竞争条件。无论哪个脚本首先执行其内部循环命令(分别为cat和echo)将阻塞并等待另一个脚本执行其内部循环命令。但是,一旦脚本同步,如果cat在echo执行其write()之前调用管道上的close(),则回送将发送一个SIGPIPE,您的脚本将退出。您无法写入已被其阅读器关闭的管道。

如果你将你的阅读器更改为tail -f而不是带有cat的while循环,那么阅读器会保持活着而不是打开并永久关闭fifo并且你不应该得到一个SIGPIPE。

参考:man fifo

答案 1 :(得分:5)

要获得非阻塞管道行为,您还可以先在fifo上打开读取文件描述符,然后打开写入文件描述符。

# cf. https://stackoverflow.com/questions/2776994/tee-a-pipe-asynchronously
(
rm -f fifo
mkfifo fifo
exec 3<fifo   # open fifo for reading
trap "exit" 1 2 3 15
exec cat fifo | nl
) &
bpid=$!

(
exec 3>fifo  # open fifo for writing
trap "exit" 1 2 3 15
while true;
do
    echo "blah" > fifo
done
)
#kill -TERM $bpid

另请参阅:How do I use exec 3>myfifo in a script, and not have echo foo>&3 close the pipe?