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

n0p*_*0pe 7 bash logging pipe named-pipes

LS:

prwx------ 1 root root 0 fifo
Run Code Online (Sandbox Code Playgroud)

write.sh:

#! /bin/bash
while true;
do
    echo "blah" > fifo
done
Run Code Online (Sandbox Code Playgroud)

read.sh:

#! /bin/bash
while true;
do
    cat fifo
done
Run Code Online (Sandbox Code Playgroud)

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

我在这里错过了什么?

fra*_*nkc 5

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

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

参考:man fifo


小智 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
Run Code Online (Sandbox Code Playgroud)

另请参阅:如何在脚本中使用exec 3> myfifo,而不是使用echo foo>&3关闭管道?