我想将一些数据输出到管道,并让另一个进程逐行对数据执行某些操作。这是一个玩具示例:
mkfifo pipe
cat pipe&
cat >pipe
现在我可以输入我想要的任何内容,按下回车后我立即看到同一行。但是如果用echo
代替第二个管道:
mkfifo pipe
cat pipe&
echo "some data" >pipe
管道在echo
和cat pipe&
完成后关闭,这样我就无法通过管道传递更多数据。有没有办法避免关闭管道和接收数据的进程,以便我可以从bash脚本通过管道传递多行数据并在它们到达时对它们进行处理?
答案 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
当然,当你想要它时,你必须杀死这个过程。我认为这是最方便的解决方案,因为它允许您在创建流程时指定不存在的行为。