Reading with cat: Stop when not receiving data
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