How to cat named pipe without waiting

catfifotail

If there is nothing in a named pipe and I do:

cat my_named_pipe

it will wait until data arrives. Is there a flag I can use to exit immediately if there is no data to be read? Or perhaps a command other than cat that I can use?

I also tried:

read val < "$my_named_pipe";

but this also waits for the next chunk of data – I don't want to wait if the fifo is empty.

Best Answer

  • To prevent cat from hanging in the absence of any writer (in which case it's the opening of the fifo, not reading from it, that hangs), you can do:

    cat 0<> "$my_named_pipe" <"$my_named_pipe"
    

    The first redirection opens in read+write mode which on most systems doesn't block and instantiates the pipe even if there's no writer nor reader already. Then the second open (read-only this time) would not block because there is at least one writer now (itself).

    The 0 is only needed in recent versions of ksh93 where the default fd for <> changed from 0 to 1.

    Also, in ksh93, that would not work when cat is the shell builtin, like when ksh93 is called when /opt/ast/bin is ahead of /bin in $PATH or after a call to builtin cat as upon the <"$my_named_pipe", (I guess) ksh93 saves the previous target of stdin on a separate file descriptor which would hold the pipe open. You can work around that by writing it instead:

    cat 3<> "$my_named_pipe" <"$my_named_pipe" 3<&-
    

    (which you might also argue conveys the intention more clearly)

    Note that that <> on the pipe would also unlock other readers to the fifo.

    If there were some writers, cat would still have to read all their output and wait until they have closed their end of the pipe. You could open the pipe in non-blocking mode, like with GNU dd's:

    dd bs=64k if="$my_named_pipe" iflag=nonblock status=noxfer
    

    Which would only read from the pipe as long as there's some data in it, and exit with a

    dd: error reading 'fifo': Resource temporarily unavailable
    

    error when there's no more, and not unlock other readers, but that means you could miss some of the writers output if they are slower to write to the pipe than you (dd) are to read it.

    Another approach could be to timeout when there's been no input in a while, for instance by using socat's -T option:

    socat -u -T1 - - 0<> "$my_named_pipe" <"$my_named_pipe"
    

    Which would exit if there's not been anything coming from the pipe in one second.

  • Related Question