开发者

Reading with cat: Stop when not receiving data

开发者 https://www.devze.com 2023-04-01 16:08 出处:网络
Is there any way to tell the cat command to stop reading when not receiving any data? maybe with some \"timeout\" that specifies for how long no dat开发者_如何转开发a is incoming.

Is there any way to tell the cat command to stop reading when not receiving any data? maybe with some "timeout" that specifies for how long no dat开发者_如何转开发a is incoming.

Any ideas?


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


Here is the code for timeout-cat:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>

void timeout(int sig) {
        exit(EXIT_FAILURE);
}

int main(int argc, char* argv[]) {
        int sec = 0; /* seconds to timeout (0 = no timeout) */
        int c;

        if (argc > 1) {
                sec = atoi(argv[1]);
                signal(SIGALRM, timeout);
                alarm(sec);
        }
        while((c = getchar()) != EOF) {
                alarm(0);
                putchar(c);
                alarm(sec);
        }
        return EXIT_SUCCESS;
}

It does basically the same as paxdiablo's dog. It works as a cat without an argument - catting the stdin. As a first argument provide timeout seconds.

One limitation (applies to dog as well) - lines are line-buffered, so you have n-seconds to provide a line (not any character) to reset the timeout alarm. This is because of readline.

usage:

instead of potentially endless:

cat < some_input > some_output

you can do compile code above to timeout_cat and:

./timeout_cat 5 < some_input > some_output


Try to consider tail -f --pid I am assuming that you are reading some file and when the producer is finished (gone?) you stop.

Example that will process /var/log/messages until watcher.sh finishes.

./watcher.sh&
tail -f /var/log/messages --pid $! | ... do something with the output


I faced same issue of cat command blocking while reading on tty port via adb shell but did not find any solution (timeout command was also not working). Below is the final command I used in my python script (running on ubuntu) to make it non-blocking. Hope this will help someone.

bash_command = "adb shell \"echo -en 'ATI0\\r\\n' > /dev/ttyUSB0 && cat /dev/ttyUSB0\" & sleep 1; kill $!"
response = subprocess.check_output(['bash', '-c', bash_command])


Simply cat then kill the cat after 5 sec.

cat xyz & sleep 5; kill $!

Get the cat output as a reply after 5 seconds

reply="`cat xyz & sleep 5; kill $!`"
echo "reply=$reply"
0

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号