使用Cat命令阅读文件:当不接收数据时停止

12

有没有办法告诉cat命令在不接收数据时停止读取?也许可以使用一些“超时”来指定多久没有数据进入。

有什么想法吗?


你难道不是在寻找“读取”吗? - Wrikken
cat $fifo | while nc ... - Kai
7个回答

7

有一个 timeout(1) 命令。例如:

timeout 5s cat /dev/random

根据您的情况而定。例如,如果您使用-e运行bash并通常关注退出代码。
timeout 5s cat /dev/random || true

如果/dev/random没有输入超过5秒钟,那么这将取消cat吗? - Kai
不,这只会停止cat命令。然后你可以分析输出来查看是否有内容。 - Michał Šrajer
但是我想要停止cat,只有当从/dev/random没有更多数据超过x秒的时候。 - Kai
了解“timeout”很好,但请注意它是_GNU_实用程序(换句话说:不是POSIX实用程序集的一部分,因此在所有平台上都不可用)。 - mklement0
@MichałŠrajer 不错的想法,不幸的是这会导致超时退出代码为124 :) - Zarathustra

5

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更加优美,如果这样能让故事听起来更好,我不介意进行一些小的修正 :-)


我觉得我不太明白。我对C语言不是很熟悉。 - Kai
当运行dog时,如果我每秒钟输入一个字母(不换行),那么我仍然会超时。换句话说 - 选择查看行缓冲数据。 - Michał Šrajer
好的,这看起来非常不错。我如何将其与像cat这样的任何东西一起使用?例如,我想说“dog /dev/input/event0”。 - Kai
@Kai,请给出更多的细节。"我尝试了X但是没有成功"并不是我见过的最详细/优美的错误报告 :-) 你看到了什么错误行为? - paxdiablo
@Kai,这与程序关系不大。我怀疑要么文件不存在,要么权限可能有误。如果您按照我的更新修改主程序,它应该会给出错误号码。 - paxdiablo
显示剩余4条评论

2

mbuffer-W 选项对我很有用。

我需要将 stdin 沉淀到文件中,但带有空闲超时:

  • 我不需要实际连接多个来源(但也许有使用 mbuffer 的方法)。
  • 我不需要任何 cat 的可能的输出格式选项。
  • 我不介意 mbuffer 提供的进度条。

我需要添加 -A /bin/false 来抑制警告,基于链接页面中的建议。我的调用复制 stdin 到文件中,带有 10 秒的空闲超时,最终看起来像这样:

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

1

这里是 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;
}

它基本上与paxdiablo的dog相同。 它作为一个没有参数的cat工作 - 将stdin连接起来。第一个参数提供超时秒数。

一个限制(也适用于dog) - 行是行缓冲的,因此您有n秒钟提供一行(而不是任何字符)以重置超时警报。这是由于readline。

用法

代替潜在的无限期:

cat < some_input > some_output

你可以将上面的编译代码命名为timeout_cat并运行:

./timeout_cat 5 < some_input > some_output

抱歉,但我无法让它工作。我尝试了"./timeout-cat /dev/input/event4 5"(event4是我的键盘)。 - Kai
这与paxdiablo的解决方案有什么区别?这只是设计问题吗? - Kai
我尝试运行"./timeout_cat 5 /dev/input/mouse0",但是在5秒后就会突然终止,没有任何输出。 - Kai
@Kai:(有什么区别)它做的事情基本相同,但方式不同。我们都在这里学习,对吧? - Michał Šrajer
好的,它开始了,但大约一秒钟后就终止了。 - Kai
显示剩余2条评论

0

尝试考虑使用tail -f --pid命令。我假设您正在读取某个文件,当生产者完成(离开?)时,您会停止。

以下是一个示例,它将处理/var/log/messages文件,直到watcher.sh完成。

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

不错。不幸的是它不在我的“tail”上。我想这只适用于Gnu。 - dubiousjim

0

我遇到了通过adb shell在tty端口上读取时cat命令阻塞的问题,但是没有找到任何解决方案(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])

我不知道如何检测数据缺失,但是难道不能使用信号来控制“cat”吗? - benc

-1

简单地将猫合并,然后在5秒钟后杀死猫。

cat xyz & sleep 5; kill $!

在5秒后作为回复获取猫的输出

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

如果数据生成过程在未指定的时间内输出数据(可能是一分钟,也可能是一天),然后停止呢?你只会得到前五秒钟的数据 - 即使源仍在输出数据。 - Piskvor left the building
是的,这是事实。因此,您必须使用此逻辑的最大超时时间。即当回复对您无用时。 - Oguz
问题在于,最大的超时时间是未知的;而收到一个通知“该进程可能已经在上周(或者也许不是,在这种情况下我们只是杀死了它)完成输出数据”,比没有任何信息更加糟糕。 - Piskvor left the building
换句话说:“我有一辆油箱加满、引擎运转的汽车。告诉我何时耗尽了燃料。” “好的,只需在固定的超时时间后关闭引擎,并假装那是正确的时刻。” 看到了吗?超时时间很可能太短(燃料还有很多),或者太长(燃料早已耗尽);你很难提前猜测出正确的时间。 - Piskvor left the building
您可以将输出从cat重定向到某个文件,并不时地轮询该文件以查看已输出的内容。然后,您可以不使用超时,而是在满意回复时杀死cat。实际上,我认为我的回答是针对页面顶部Kai的问题的。您的情况则另当别论。 - Oguz
有没有办法告诉cat命令在不接收任何数据时停止读取?也许可以使用一些“超时”来指定多长时间没有数据传入。“稍等片刻,盲目地假设没有更多的数据传入,然后就完成了。”当然,这回答了问题。这不是一个非常有用的答案,大多数情况下会给出错误的结果,但它回答了问题。 - Piskvor left the building

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接