Reading with cat: Stop when not receiving data Reading with cat: Stop when not receiving data linux linux

Reading with cat: Stop when not receiving data


There is a timeout(1) command. Example:

timeout 5s cat /dev/random

Dependening on your circumstances. E.g. you run bash with -e and care normally for the exit code.

timeout 5s cat /dev/random || true


cat itself, no. It reads the input stream until told it's the end of the file, blocking for input if necessary.

There's nothing to stop you writing your own cat equivalent which will use select on standard input to timeout if nothing is forthcoming fast enough, and exit under those conditions.

In fact, I once wrote a snail program (because a snail is slower than a cat) which took an extra argument of characters per second to slowly output a file (a).

So snail 10 myprog.c would output myprog.c at ten characters per second. For the life of me, I can't remember why I did this - I suspect I was just mucking about, waiting for some real work to show up.

Since you're having troubles with it, here's a version of dog.c (based on my afore-mentioned snail program) that will do what you want:

#include <stdio.h>#include <unistd.h>#include <errno.h>#include <sys/select.h>static int dofile (FILE *fin) {    int ch = ~EOF, rc;    fd_set fds;    struct timeval tv;    while (ch != EOF) {        // Set up for fin file, 5 second timeout.        FD_ZERO (&fds); FD_SET (fileno (fin), &fds);        tv.tv_sec = 5; tv.tv_usec = 0;        rc = select (fileno(fin)+1, &fds, NULL, NULL, &tv);        if (rc < 0) {            fprintf (stderr, "*** Error on select (%d)\n", errno);            return 1;        }        if (rc == 0) {            fprintf (stderr, "*** Timeout on select\n");            break;        }        // Data available, so it will not block.        if ((ch = fgetc (fin)) != EOF) putchar (ch);    }    return 0;}

 

int main (int argc, char *argv[]) {    int argp, rc;    FILE *fin;    if (argc == 1)        rc = dofile (stdin);    else {        argp = 1;        while (argp < argc) {            if ((fin = fopen (argv[argp], "rb")) == NULL) {                fprintf (stderr, "*** Cannot open input file [%s] (%d)\n",                    argv[argp], errno);                return 1;            }            rc = dofile (fin);            fclose (fin);            if (rc != 0)                break;            argp++;        }    }    return rc;}

Then, you can simply run dog without arguments (so it will use standard input) and, after five seconds with no activity, it will output:

*** Timeout on select

(a) Actually, it was called slowcat but snail is much nicer and I'm not above a bit of minor revisionism if it makes the story sound better :-)


mbuffer, with its -W option, works for me.

I needed to sink stdin to a file, but with an idle timeout:

  • I did not need to actually concatenate multiple sources (but perhaps there are ways to use mbuffer for this.)
  • I did not need any of cat's possible output-formatting options.
  • I did not mind the progress bar that mbuffer brings to the table.

I did need to add -A /bin/false to suppress a warning, based on a suggestion in the linked man page. My invocation for copying stdin to a file with 10 second idle timeout ended up looking like

mbuffer -A /bin/false -W 10 -o ./the-output-file