2011-12-07 117 views
40

我想输出一些数据到一个管道,让另一个进程对数据一行一行地做些什么。这里是一个玩具例子:如何避免回声关闭FIFO命名管道? - Unix FIFO的有趣行为

mkfifo pipe 
cat pipe& 
cat >pipe 

现在我可以进入任何我想要的,然后按Enter键后,我立刻看到同一行。但是,如果替代第二管道与echo

mkfifo pipe 
cat pipe& 
echo "some data" >pipe 

管道后echocat pipe&完成关闭,这样我可以不通过管道传递任何更多的数据。有没有办法避免关闭管道和接收数据的进程,以便我可以通过管道从bash脚本传递多行数据,并在它们到达时对它们进行处理?

回答

38

当FIFO打开读取时,它会阻塞调用进程(通常)。当进程打开FIFO进行写入时,读取器将被解除阻塞。当写入程序关闭FIFO时,读取过程获得EOF(0字节读取),除了关闭FIFO并重新打开外,没有什么可以做的了。因此,您需要使用一个循环:

mkfifo pipe 
(while cat pipe; do : Nothing; done &) 
echo "some data" > pipe 
echo "more data" > pipe 

另一种方法是保持某个进程打开FIFO。

mkfifo pipe 
sleep 10000 > pipe & 
cat pipe & 
echo "some data" > pipe 
echo "more data" > pipe 
+0

第二个版本则方位的工作!第一个不适合我,因为我不想重新启动接收数据的进程。 – user1084871

+9

您可能可以作弊,并让'cat'通过使用:'cat pipe 3> pipe'将管道打开以便写入。 'cat'命令不会使用文件描述符3,但是将会有一个叫做管道的FIFO,用于写入(尽管它将在另一个文件描述符 - 可能是4上读取它)。 –

+1

'exec> 6 pipe'没有达到同样的效果吗?基本上将'pipe'分配给文件描述符6并保持写入状态。而不是直接写入'pipe',你可能希望使用'>&6'写入描述符,否则它应该保持它打开iirc – Haravikk

46

把所有你想要的报表输出到FIFO在同一子shell:

# Create pipe and start reader. 
mkfifo pipe 
cat pipe & 
# Write to pipe. 
(
    echo one 
    echo two 
) >pipe 

如果你有一些更复杂,你可以打开管道写入:

# Create pipe and start reader. 
mkfifo pipe 
cat pipe & 
# Open pipe for writing. 
exec 3>pipe 
echo one >&3 
echo two >&3 
# Close pipe. 
exec 3>&- 
+1

优秀的答案,并且实际上成功地保持管道在任意复杂的命令序列中打开。 – voetsjoeba

+5

你能简单介绍一下它是如何工作的吗?特别是最后一行:'exec 3>& - ' –

+2

@NickChammas:在第二个例子中,exec3管道打开文件描述符3写入'pipe';两个'echo'命令通过输出重定向写入管道;最后一个'exec 3>& - '是在这种情况下如何关闭一个打开的文件描述符 - 描述符3。那时候,在后台运行的'cat'会得到一个EOF并终止。 –

1

作为此处其他解决方案的替代方案,您可以在循环中将cat作为您的命令的输入:

mkfifo pipe 
(while true ; do cat pipe ; done) | bash 

现在你可以给它一个指令的时间和它不会关闭:

echo "'echo hi'" > pipe 
echo "'echo bye'" > pipe 

你必须杀死,当你想它了,当然,过程。我认为这是最方便的解决方案,因为它可以让您在创建过程时指定非退出行为。

1

我增强从乔纳森莱弗勒的回答第二个版本,支持关闭管道:

dir=`mktemp -d /tmp/temp.XXX` 
keep_pipe_open=$dir/keep_pipe_open 
pipe=$dir/pipe 

mkfifo $pipe 
touch $keep_pipe_open 

# Read from pipe: 
cat < $pipe & 

# Keep the pipe open: 
while [ -f $keep_pipe_open ]; do sleep 1; done > $pipe & 

# Write to pipe: 
for i in {1..10}; do 
    echo $i > $pipe 
done 

# close the pipe: 
rm $keep_pipe_open 
wait 

rm -rf $dir 
14

您可以通过在读写模式下打开管道的读取端很容易解决这个问题。读者只有在最后一位作家关闭后才能获得EOF。因此以读写方式打开它可以确保至少有一个作者。

所以,你的第二个例子更改为:

mkfifo pipe 
cat <>pipe & 
echo "some data" >pipe 
+1

这显然是唯一正确的答案。谢谢。 –

+1

用这种方法我不能解决如何关闭管道的问题,我只能杀死那些可能表现不一样的猫进程。例如,如果cat实际上是一个带有END块的awk程序,那么END块在接收到SIGTERM时不会执行。 – pix