如何避免echo关闭FIFO命名管道? - Unix FIFO的搞笑行为

时间:2011-12-07 04:36:31

标签: bash unix pipe echo

我想将一些数据输出到管道,并让另一个进程逐行对数据执行某些操作。这是一个玩具示例:

mkfifo pipe
cat pipe&
cat >pipe

现在我可以输入我想要的任何内容,按下回车后我立即看到同一行。但是如果用echo代替第二个管道:

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

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

6 个答案:

答案 0 :(得分:52)

将要输出的所有语句放在同一个子shell中的fifo中:

# 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 :(得分:45)

当打开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

答案 2 :(得分:20)

通过在读写模式下打开管道的读取端,可以非常轻松地解决这个问题。一旦最后一位作家关闭,读者只能得到一个EOF。因此,在读写中打开它可确保始终至少有一个编写器。

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

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

答案 3 :(得分:2)

我从Jonathan Leffler的答案中增强了第二个版本以支持关闭管道:

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

答案 4 :(得分:2)

老实说,我能够使它工作的最好方法是使用socat,它基本上连接了两个套接字。

mkfifo foo
socat $PWD/foo /dev/tty

现在,您可以:

echo "I am in your term!" > foo
# also (surprisingly) this works
clear > foo

缺点是您需要socat,这不是每个人都能获得的基本工具。好的一面是,我找不到不起作用的东西...我能够打印颜色,tee到fifo,清除屏幕等。就好像您奴役了整个终端一样

答案 5 :(得分:1)

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

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

现在,您可以一次提供一个命令,但不会关闭:

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

当然,当你想要它时,你必须杀死这个过程。我认为这是最方便的解决方案,因为它允许您在创建流程时指定不存在的行为。