Bash 来自FIFO的多个读卡器

Bash 来自FIFO的多个读卡器,bash,stdin,fifo,Bash,Stdin,Fifo,是否有可能在多个读卡器之间拆分STDIN,从而有效地成为一个作业队列?我想把每一行都传给一个读者。命名管道几乎可以工作,但同时读取会产生干扰: reader.sh #!/usr/bin/env bash while read line do echo $line done < fifo 执行: mkfifo fifo ./reader.sh & ./reader.sh & ./writer.sh > fifo mkfifo fifo ./reader.sh

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

reader.sh

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

mkfifo fifo
./reader.sh &
./reader.sh &
./writer.sh > fifo
mkfifo fifo
./reader.sh &
./reader.sh &
./writer.sh > fifo
偶尔输出(特别是当读写器位于单独的窗口中时)

注:

  • 我知道有更好的方法,只是好奇这是否能奏效
  • 我假设这不是一个bug,因为我已经测试了Linux和OSX设备
  • 我希望每条线有一个消费者,这就排除了T恤
  • 我想使用STDIN,它排除了xargs
  • GNU coreutils split可以分配循环,但不能先分配
  • GNU parallel——管道等待标准输入关闭;我想尽快分配

您可以将读卡器更改为

#!/usr/bin/env bash
cat fifo | while read line
do
  echo $line
done
这样,它将读取整行或什么都不读取

另一个版本的问题是,从fifo读取的责任是内置的
读取
,它使用1个字符的缓冲区进行读取,因此,如果两个进程同时运行,同一行的不同字符可以被两个进程读取。您可以通过
strace
查看它:

strace bash -c 'while read line; do echo $line; done < fifo'`

但是,我不建议将其用作作业队列,因为它似乎无法在所有读卡器之间均匀地分配读取。

不,通常不可能可靠地执行此操作。对小于pipe_BUF(在所有POSIX系统上大于等于512字节)的命名管道的写入是原子的。问题是读取不是原子的,并且没有标准(或非标准AFAIK)方法使其原子化。在对管道进行阻塞读取时,如果有1个或多个字节可用,将立即读取这些字节,并返回实际读取数作为返回值

国家:

因为不能保证原子性,所以除非有另一种并发控制机制,否则决不能允许多个读卡器。。。。改用消息队列之类的东西

话虽如此,为了好玩,我们有可能实现令人惊讶的强健行为。原因是基于行的
cat;,而读取行do方法”之所以有效,是因为
cat
一到就立即从管道中抓取行,正如您所提到的,读者一开始写作就准备好阅读。因为它是直接读取的,所以它恰好在它们被写入的行边界处抓取行(复数)。一般来说,基于行的方法不会非常健壮,因为消息边界是不可预测的


如果你以固定大小的块
写和读,我在这里复制了这个(OSX),但我不明白,我想我明白了。虽然对管道的写入是原子的(只要它们小于
BUFSIZ
),但消息边界不会保存在管道中。因此,并发读卡器可以分别读取输入的不同部分。从流中读取的系统调用没有提供任何方法来请求作为一个单元的整个行。看,我认为管道是错误的机制。消息队列或数据报套接字会更好,尽管它们在
bash
中没有很好的接口。Yuo可能需要用Perl、PHP或Python编写脚本。您可以调用GNU Parallel作为
sem--id mymutex
作为
read
命令的前缀,以便它们一次执行一个。此外,cat将读取STDIN中的所有内容,因此如果存在,将清空整个多行队列。所有单行命令(head、sed、awk)在读取后退出,关闭管道。是否有一个每次连续读取整行的命令(如果只有head有一个-f--follow选项,比如tail)?如果缓冲区中存在更多行,则不可能只读取一行,因为raw
read()
操作是通过读取给定数量的字节来工作的,而不是在找到某种字符之前读取。只读取一行的程序一次读取一个字符(正如我在内置int
read
的答案中所解释的),因此它们将与其他程序交错,导致您试图解决的问题。我建议重新编写答案。第一句话表明没有简单的解决方案,最后给出了工作解决方案。我跳过了答案,自己找到了固定大小记录的解决方案。我重新编写了,但答案仍然是你不能完全稳健地完成,所以。。。使用消息队列或类似队列。该解决方案似乎在大多数情况下都有效,但在某些情况下可能无效。这取决于您;-)但我会这样写:“是的,总的来说这是可能的,但没有可靠的解决方案。”
strace bash -c 'while read line; do echo $line; done < fifo'`
strace cat fifo | while read line; do echo $line; done
#!/bin/bash
while read -N 21 packet
do
  echo [$$] $packet
done<fifo
#!/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