是否可以在多个读卡器之间拆分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
注意:
答案 0 :(得分:1)
总的来说,不可能做得很好。写入低于PIPE_BUF的命名管道(在所有POSIX系统上&gt; = 512字节)是原子的。问题是读取不是原子的,并且没有标准(或非标准AFAIK)方法来制作它们。如果有一个或多个字节可用,则在阻塞读取管道时,将立即读取它们,并将实际数字读取作为返回值返回。
说了这么多,为了好玩,有可能实现令人惊讶的强健行为。基于行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}}
但是,我不建议将其用作作业队列,因为它似乎不会在读者之间均匀分布读取。