Чтение с кошкой: остановка при отсутствии данных - PullRequest
9 голосов
/ 01 сентября 2011

Можно ли как-нибудь сказать команде cat прекратить чтение, если не получено никаких данных?может быть, с некоторым «тайм-аутом», который указывает, как долго данные не поступают.

Есть идеи?

Ответы [ 7 ]

5 голосов
/ 01 сентября 2011

Существует команда timeout(1).Пример:

timeout 5s cat /dev/random
5 голосов
/ 01 сентября 2011

cat само по себе, нет. Он считывает входной поток до тех пор, пока ему не сообщат, что это конец файла, при необходимости блокируя ввод.

Ничто не помешает вам написать свой собственный cat эквивалент, который будет использовать select на стандартном вводе для тайм-аута, если ничего не произойдет достаточно быстро, и завершить работу в этих условиях.

Фактически, я однажды написал программу snail (потому что улитка медленнее кошки), которая принимала дополнительный аргумент символов в секунду, чтобы медленно выводить файл (a) .

Таким образом, snail 10 myprog.c будет выводить myprog.c со скоростью десять символов в секунду. Что касается жизни, я не могу вспомнить, почему я это сделал - я подозреваю, что просто шутил, ожидая, когда появится какая-то настоящая работа.

Так как у вас проблемы с этим, вот версия dog.c (основанная на моей вышеупомянутой программе snail), которая будет делать то, что вы хотите:

#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;
}

Затем вы можете просто запустить dog без аргументов (поэтому он будет использовать стандартный ввод) и через пять секунд бездействия выведет:

*** Timeout on select

(a) На самом деле, он назывался slowcat, но snail намного приятнее, и я не против небольшого ревизионизма, если он заставляет историю звучать лучше: -)

1 голос
/ 01 сентября 2011

Вот код для 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;
}

По сути, это то же самое, что и собака Паксдиабло. Он работает как cat без аргументов - перехватывает стандартный ввод. В качестве первого аргумента укажите время ожидания в секундах.

Одно ограничение (также относится и к собаке) - строки буферизуются, поэтому у вас есть n-секунд, чтобы предоставить строку (не любой символ) для сброса тревоги тайм-аута. Это из-за readline.

использование

вместо потенциально бесконечного:

cat < some_input > some_output

Вы можете сделать код выше для timeout_cat и:

./timeout_cat 5 < some_input > some_output
0 голосов
/ 16 января 2019

Я столкнулся с той же проблемой блокировки команды cat при чтении через порт tty через оболочку adb, но не нашел решения (команда timeout также не работала).Ниже приведена последняя команда, которую я использовал в своем скрипте Python (работающем в Ubuntu), чтобы сделать его неблокирующим.Надеюсь, это кому-нибудь поможет.

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])
0 голосов
/ 08 ноября 2017

mbuffer, с его опцией -W, работает для меня.

Мне нужно было вставить stdin в файл, но с таймаутом простоя:

  • Мне не нужно было объединять несколько источников (но, возможно, есть способы использовать mbuffer для этого.)
  • Мне не нужны были какие-либо возможные варианты форматирования вывода cat.
  • Я не возражал против индикатора выполнения, который mbuffer приносит на стол.

Мне нужно было добавить -A /bin/false для подавления предупреждения, основанного на предложении на связанной справочной странице. Мой вызов для копирования stdin в файл с 10-секундным тайм-аутом в итоге выглядел как

mbuffer -A /bin/false -W 10 -o ./the-output-file
0 голосов
/ 02 марта 2016

Просто кошка, а затем убить кошку через 5 секунд.

cat xyz & sleep 5; kill $!

Получить вывод cat в качестве ответа через 5 секунд

reply="`cat xyz & sleep 5; kill $!`"
echo "reply=$reply"
0 голосов
/ 18 октября 2012

Попытайтесь рассмотреть tail -f --pid Я предполагаю, что вы читаете какой-то файл, и когда продюсер закончил (ушел?), Вы останавливаетесь.

Пример, который будет обрабатывать / var / log / messagesпока не закончится watcher.sh.

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