来自FIFO的多个读卡器

时间:2015-10-21 22:14:23

标签: bash stdin fifo

是否可以在多个读卡器之间拆分STDIN,有效地成为一个作业队列?我想将每一行传递给一个读者。命名管道几乎工作,但同时读取干扰:

reader.sh

#!/usr/bin/env bash
while read line
do
  echo $line
done <  fifo

writer.sh

#!/usr/bin/env bash
while true
do
  echo "This is a test sentance"
  sleep 1
done

执行:

mkfifo fifo
./reader.sh &
./reader.sh &
./writer.sh > fifo

偶尔的输出(特别是如果读者和作者在不同的窗口中)

This is atetsnac
Ti sats etnesats etne etsnac
isats etnes etsnac
Tisi etsnac
hi etsnac
Ti sats etn
hsi etsnac

注意:

  • 我知道有更好的方法,只是好奇这是否可以使用
  • 我认为这不是一个错误,因为我已经测试了Linux和OSX盒子
  • 我希望每行一个消费者,排除T恤
  • 我想要消耗STDIN,它排除了xargs
  • GNU coreutils split可以分配循环,但不是第一次可用
  • GNU parallel --pipe等待STDIN关闭;我想尽快分配

2 个答案:

答案 0 :(得分:1)

总的来说,不可能做得很好。写入低于PIPE_BUF的命名管道(在所有POSIX系统上&gt; = 512字节)是原子的。问题是读取不是原子的,并且没有标准(或非标准AFAIK)方法来制作它们。如果有一个或多个字节可用,则在阻塞读取管道时,将立即读取它们,并将实际数字读取作为返回值返回。

M.Rochkind指出&#34;因为不能保证原子性,所以除非你有另一种并发控制机制,否则你绝不允许多个读者....改为使用类似消息队列的东西。&#34; - Advance UNIX Programming

说了这么多,为了好玩,有可能实现令人惊讶的强健行为。基于行cat | while read line do; ..逼近的行似乎起作用的原因是因为cat一旦到达就会立即从管道中抢走行,并且读者可以尽快阅读正如你所提到的,写作开始了。因为它正在直接读取,所以它恰好在它们被写入的行边界处抢夺行(复数)。一般而言,基于行的方法不会非常健壮,因为消息边界是不可预测的。

如果您使用常量大小的块&lt; = PIPE_BUF进行编写和阅读,您可以做得更好。你保证永远不会阅读超过你的要求,并且只要你编写的是常量大小的块,每次写入时大小小于PIPE_BUF,就没有理由认为应该有一小块字节可用于读 - 除了它没有保证的事实,因此可能会由于某种原因发生。

reader.sh:

#!/bin/bash
while read -N 21 packet
do
  echo [$$] $packet
done<fifo

writer.sh

#!/bin/bash
for((i=0; i<100; i++))
do
  s=`printf "%020d" $i`
  echo $s
  echo "wrote $s" >&2
done

执行:

mkfifo fifo
./reader.sh &
./reader.sh &
./writer.sh > fifo

答案 1 :(得分:-1)

您可以将阅读器更改为

read

这样,它会读取整行或什么都没有。

另一个版本的问题是从fifo读取的责任是内置strace,它使用1个字符的缓冲区进行读取,因此可以读取同一行的不同字符如果它们同时运行,则通过两个进程。您可以使用strace bash -c 'while read line; do echo $line; done < fifo'`

查看
cat

strace cat fifo | while read line; do echo $line; done 使用更大的缓冲区来读取,因此它最终会接收整行。用以下方法测试:

{{1}}

但是,我不建议将其用作作业队列,因为它似乎不会在读者之间均匀分布读取。