2015-10-15 64 views
1

看看下面的代码:击重定向:命名管道和EOF

rm -f pipe 
mkfifo pipe 

foo() { 
    echo 1 
    sleep 1 
    echo 2 
} 

#1 
exec 3< <(foo &) 
cat <&3 # works 

#2 
foo >pipe & 
cat <pipe # works 

#3 
exec 3<>pipe 
foo >&3 & 
cat <&3 # hangs 

#4 -- update: this is the correct approach for what I want to do 
foo >pipe & 
exec 3<pipe 
rm pipe 
cat <&3 # works 

为什么做法#3挂起,而有的则没有?有没有办法让方法#3不挂?

理由:我希望用准命名管道连接多个异步运行子过程,为了这个,我需要做一个文件描述符指向它后删除管:在方法3

mkfifo pipe 
exec {fd}<>pipe 
rm pipe 
# use &$fd only 

回答

2

问题是FIFO pipe然后有2个写入器:bash脚本(因为您已使用exec 3<>打开了它的读取/写入)和运行foo的子shell。当所有编写者关闭文件描述符时,您将阅读EOF。一个作者(运行foo的子shell)将很快(大约1秒后)退出,因此关闭文件描述符。然而,另一个作者(主外壳)仅在它退出时关闭文件描述符,因为在任何地方都没有关闭文件描述符3。但它不能退出,因为它等待cat先退出。这是一个僵局:

  • cat等待一个EOF
  • 的EOF只有当主壳关闭
  • 主壳等待cat的FD(或出口)终止
出现

因此,你永远不会退出。

情况2的工作原理是因为管道只有一个写入器(运行foo的子shell)很快退出,因此将读取EOF。在情况1中,也只有一个作者,因为你打开fd 3只读(exec 3<)。

编辑:删除关于案例4的无稽之谈不正确(见评论)。这是正确的,因为作者无法在阅读器连接之前退出,因为在阅读器尚未打开时打开文件时它也会被阻止。 不幸的是,新添加的案例4不正确。它很活泼,只有在exec 3<pipe运行前foo没有终止(或关闭管道)时才起作用。

同时检查fifo(7)手册页:

内核维护只有一个管道对象由至少一个进程打开的每个FIFO特殊文件。在数据通过之前,FIFO必须在两端打开(读和写)。通常,打开FIFO模块直到另一端也打开。

+0

钉住它。 'exec 3 <>'使得主外壳成为编写器,这导致了EOF问题。 – Irfy

+0

@Iffy我补充说。在情况1中,文件描述符3在主外壳中以只读方式打开。因此,管道永远不会有超过1个作者。 –

+0

在此帮助下,我重写了删除管道的代码,以便按预期工作,谢谢。 – Irfy