Home > Blockchain >  Capture latest write to file/FIFO buffer and then flush the file/FIFO buffer
Capture latest write to file/FIFO buffer and then flush the file/FIFO buffer

Time:03-09

Consider the following bash example:

#!/bin/bash

ping -i 0.1 1.1.1.1 | while read ping_line
do
        echo $ping_line
        sleep 1
done

The processing on the right-hand side of the pipe is slower than the rate of the incoming pings on the left-hand side of the pipe. Here, the 'sleep 1' is intended to simulate a processing delay. And so the ping results that are output increasingly lag behind the incoming ping results.

So what if instead we want to always output the latest available ping line, and, only if necessary because processing is too slow, skip any intervening lines?

In particular, in bash how would you have ping write out to a file or fifo buffer, and then set up a while loop on that file or fifo buffer that echos the latest available ping line and flushes the file or fifo buffer after every read, ready for the next read of the latest available ping line?

So in terms of buffering, ideally the buffer is read from at the rate the values come in, but if there is any delay and the buffer grows to say [0 1 2 3], then we take '3' and discard '0', '1' and '2' for that read cycle. Because '3' is the latest available ping line.

Alternatively, if would also be acceptable to have ping blocked right after outputting one line, which line is then echoed, and then ping is unblocked, etc.

The idea is to keep a ping process going and each loop cycle work with the latest available ping line without a buffer or file size growing in perpetuity, hence the need to flush the file or fifo, or some other appropriate mechanism.

Here is my failed attempt:

#!/bin/bash

pipe=/tmp/test
mkfifo $pipe

ping -i 0.1 1.1.1.1 > $pipe&
sleep 1

while read ping_line <$pipe
do
        echo $ping_line
        sleep 1
        dd if=$pipe iflag=nonblock of=/dev/null 2> /dev/null
done

I'd like it to echo the last write to the fifo buffer, then flush the fifo buffer, and repeat. So in this case it should output about every 10nth ping output line.

CodePudding user response:

Use perl's sysread. eg:

while sleep 1; do printf "$((i  )): "; date; done | 
while sleep 3; do
    perl -E 'sysread STDIN, my $s, 1024; @a=split /\n/, $s; say @a[-1] '
done

Note that this will not "flush" the fifo, but it will consume a good chunk. You can tune the 1024, or add a while loop to consume everything. (You can't flush an input stream, all you can do is read it to exhaustion.). Since you presumably don't care too much about losing data, you can consume the stream with something like:

perl -e 'my $r = sysread STDIN, my $s, 1024 while( $r == 1024 )'

(Just add that before the perl that actually prints anything)

CodePudding user response:

As a possible solution, how about:

#!/bin/bash

pipe=/tmp/test
mkfifo $pipe

ping -D -i 0.1 1.1.1.1 > $pipe&

cat $pipe > /dev/null&

while true
do
        head -1 $pipe
        sleep 1
done

  • Related