fish shell 和 bash 在处理命名管道时有什么区别?

What are the differences between fish shell and bash when handling named pipes?

在 fish 中执行这些命令时 shell

$ mkfifo answer
$ nc -vv -l -k -p 8001 <answer | tee -a answer

命令挂起。
如果我通过 echo "" > answer 写信给 answer。然后 nc 恢复并开始正确收听。
如果,相反我CTRL-C挂了进程,这里是消息:

^C<W> fish: An error occurred while redirecting file 'answer'
open: Interrupted system call

另一方面,在bash中,执行时:

$ mkfifo answer
$ nc -vv -l -k -p 8001 <answer | tee -a answer
Listening on localhost 8001

命令不会挂起并直接开始侦听。

鱼和 bash 有什么不同可以解释这种不同的行为?

主要区别在于 fish 在 parent shell 中 fork 之前打开重定向文件,而 bash 在 fork 之后打开重定向文件,在 fork 中child 进程。

当您打开命名管道进行读取时,打开调用将阻塞,直到有相应的写入器(反之亦然)。您的管道同时包含 reader 和 writer,但要求相应的打开调用在 child 进程中并行发生。在 fish 中 nc 在文件打开之前无法启动,因此出现死锁。

一种解决方法是安排 child 进程自行打开文件,而不是使用重定向。例如,这将避免死锁:

cat answer | nc -vv -k -l -p 8001  | tee -a answer

fish 这样做是因为它也使用线程,这限制了您在分叉 child.

中可以做的事情