bash 'grep -q' 不以 'tail -f' 退出

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/7178888/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-18 00:39:49  来源:igfitidea点击:

'grep -q' not exiting with 'tail -f'

linuxbashgrep

提问by AvadhP

I am trying to implement a script that wait for a specific message in a log file. Once the message is logged then I want to continue the script.

我正在尝试实现一个脚本来等待日志文件中的特定消息。记录消息后,我想继续编写脚本。

Here's what I am trying out with tail -fand grep -q:

这是我正在尝试的tail -fgrep -q

# tail -f logfile | grep -q 'Message to continue'

The grepnever quit and so it waits forever even if 'Message to continue' is logged in the file.

grep从没有放弃过,所以它永远等待,即使“消息继续”被记录在文件中。

When I run this without -fit seems to work fine.

当我在没有-f它的情况下运行它时,它似乎工作正常。

回答by knittl

tail -fwill read a file and display lines later added, it will not terminate (unless a signal like SIGTERMis sent). grepis not the blocking part here, tail -fis. grepwill read from the pipe until it is closed, but it never is because tail -fdoes not quit and keep the pipe open.

tail -f将读取文件并显示稍后添加的行,它不会终止(除非发送类似信号SIGTERM)。grep不是这里的阻塞部分,tail -f是。grep将从管道中读取直到它关闭,但它永远不会因为tail -f不退出并保持管道打开。



A solution to your problem would probably be (not tested and very likely to perform badly):

您的问题的解决方案可能是(未经测试并且很可能表现不佳):

tail -f logfile | while read line; do
  echo $line | grep -q 'find me to quit' && break;
done

回答by Jonathan Leffler

After some experimentation, I believe the problem is in the way that bashwaits for all the processes in a pipeline to quit, in some shape or form.

经过一些实验,我认为问题在于bash等待管道中的所有进程以某种形式退出的方式。

With a plain file 'qqq' of some 360 lines of C source (a variety of program concatenated several times over), and using 'grep -q return', then I observe:

使用大约 360 行 C 源代码的纯文件“qqq”(多次连接的各种程序),并使用“grep -q return”,然后我观察到:

  1. tail -n 300 qqq | grep -q returndoes exit almost at once.
  2. tail -n 300 -f qqq | grep -q returndoes not exit.
  3. tail -n 300 -f qqq | strace -o grep.strace -q returndoes not exit until interrupted. The grep.stracefile ends with:

    read(0, "#else\n#define _XOPEN_SOURCE 500\n"..., 32768) = 10152
    close(1)                                = 0
    exit_group(0)                           = ?
    

    This is one leads me to think that grephas exited before the interrupt kills tail; if it was waiting for something, there would be an indication that it received a signal.

  4. A simple program that simulates what the shell does, but without the waiting, indicates that things terminate.

    #define _XOPEN_SOURCE 600
    #include <stdlib.h>
    #include <unistd.h>
    #include <stdarg.h>
    #include <errno.h>
    #include <string.h>
    #include <stdio.h>
    
    static void err_error(const char *fmt, ...)
    {
        int errnum = errno;
        va_list args;
        va_start(args, fmt);
        vfprintf(stderr, fmt, args);
        va_end(args);
        if (errnum != 0)
            fprintf(stderr, "%d: %s\n", errnum, strerror(errnum));
        exit(1);
    }
    
    int main(void)
    {
        int p[2];
        if (pipe(p) != 0)
            err_error("Failed to create pipe\n");
        pid_t pid;
        if ((pid = fork()) < 0)
            err_error("Failed to fork\n");
        else if (pid == 0)
        {
            char *tail[] = { "tail", "-f", "-n", "300", "qqq", 0 };
            dup2(p[1], 1);
            close(p[0]);
            close(p[1]);
            execvp(tail[0], tail);
            err_error("Failed to exec tail command");
        }
        else
        {
            char *grep[] = { "grep", "-q", "return", 0 };
            dup2(p[0], 0);
            close(p[0]);
            close(p[1]);
            execvp(grep[0], grep);
            err_error("Failed to exec grep command");
        }
        err_error("This can't happen!\n");
        return -1;
    }
    

    With a fixed size file, tail -fisn't going to exit - so the shell (bash) seems to hang around.

  5. tail -n 300 -f qqq | grep -q returnhung around, but when I used another terminal to add another 300 lines to the file qqq, the command exited. I interpret this as happening because grephad exited, so when tailwrote the new data to the pipe, it got a SIGPIPE and exited, and bashtherefore recognized that all the processes in the pipeline were dead.
  1. tail -n 300 qqq | grep -q return几乎立即退出。
  2. tail -n 300 -f qqq | grep -q return不退出。
  3. tail -n 300 -f qqq | strace -o grep.strace -q return直到被中断才退出。该grep.strace文件以:

    read(0, "#else\n#define _XOPEN_SOURCE 500\n"..., 32768) = 10152
    close(1)                                = 0
    exit_group(0)                           = ?
    

    这是一个让我认为grep在中断杀死之前已经退出的tail;如果它在等待什么,就会有迹象表明它收到了信号。

  4. 一个简单的程序模拟 shell 所做的事情,但没有等待,表明事情终止了。

    #define _XOPEN_SOURCE 600
    #include <stdlib.h>
    #include <unistd.h>
    #include <stdarg.h>
    #include <errno.h>
    #include <string.h>
    #include <stdio.h>
    
    static void err_error(const char *fmt, ...)
    {
        int errnum = errno;
        va_list args;
        va_start(args, fmt);
        vfprintf(stderr, fmt, args);
        va_end(args);
        if (errnum != 0)
            fprintf(stderr, "%d: %s\n", errnum, strerror(errnum));
        exit(1);
    }
    
    int main(void)
    {
        int p[2];
        if (pipe(p) != 0)
            err_error("Failed to create pipe\n");
        pid_t pid;
        if ((pid = fork()) < 0)
            err_error("Failed to fork\n");
        else if (pid == 0)
        {
            char *tail[] = { "tail", "-f", "-n", "300", "qqq", 0 };
            dup2(p[1], 1);
            close(p[0]);
            close(p[1]);
            execvp(tail[0], tail);
            err_error("Failed to exec tail command");
        }
        else
        {
            char *grep[] = { "grep", "-q", "return", 0 };
            dup2(p[0], 0);
            close(p[0]);
            close(p[1]);
            execvp(grep[0], grep);
            err_error("Failed to exec grep command");
        }
        err_error("This can't happen!\n");
        return -1;
    }
    

    使用固定大小的文件,tail -f不会退出 - 所以外壳 ( bash) 似乎徘徊。

  5. tail -n 300 -f qqq | grep -q return徘徊,但是当我使用另一个终端向文件添加另外 300 行时qqq,命令退出。我将这解释为发生了,因为grep已经退出,所以当tail将新数据写入管道时,它得到一个 SIGPIPE 并退出,bash因此认识到管道中的所有进程都已死。

I observed the same behaviour with both kshand bash. This suggests it is not a bug but some expected behaviour. Testing on Linux (RHEL 5) on an x86_64 machine.

我观察到与ksh和相同的行为bash。这表明它不是错误,而是一些预期的行为。在 x86_64 机器上的 Linux (RHEL 5) 上进行测试。

回答by mmaruska

tail -f logfile | grep  --max-count=1  -q 'Message to continue'

Admittedly, it exits when the next line is read, not immediately on the matched one.

诚然,它在下一行被读取时退出,而不是在匹配的一行上立即退出。

回答by Kevin

I thought I'd post this as an answer since it explains why the command exits after a second write to the file:

我想我会将此作为答案发布,因为它解释了为什么命令在第二次写入文件后退出:

touch xxx
tail -f xxx | grep -q 'Stop'
ps -ef |grep 'grep -q'
# the grep process is there
echo "Stop" >> xxx
ps -ef|grep 'grep -q'
# the grep process actually DID exit
printf "\n" >> xxx
# the tail process exits, probably because it receives a signal when it 
# tries to write to a closed pipe

回答by silverjam

That's because tailwith the -f(follow) option doesn't quit, and continues to provide output to grep. Waiting for lines in a log file would probably be easier with perl/python.

这是因为tail-f(后续)选择不退出,并继续提供输出grep。使用 perl/python 可能更容易等待日志文件中的行。

Launch tail -fwith the Python subprocess module. Read output from tailin a loop until you see the lines you want then exit the Python script. Put this solution inside your shell script.

tail -f使用 Python 子进程模块启动。从tail循环中读取输出,直到看到所需的行,然后退出 Python 脚本。将此解决方案放入您的 shell 脚本中。

The Python script will block the shell script until the desired lines are seen.

Python 脚本将阻止 shell 脚本,直到看到所需的行。

回答by Eric Mockler

I was searching for the answer to this for my own project. Trying to test when exactly the passed through GPU becomes active on a VMware ESXi VM. Multiple variations of the same question are everywhere. This one is pretty recent. I figured out a way to fool it, and if you can live with your interesting line repeated in the log then:

我正在为我自己的项目寻找答案。尝试测试通过的 GPU 在 VMware ESXi 虚拟机上何时处于活动状态。同一问题的多种变体无处不在。这个是最近的。我想出了一种方法来愚弄它,如果你能忍受在日志中重复你有趣的行,那么:

tail -n 1 -f /var/log/vmkernel.log | grep -m 1 IOMMUIntel >>/var/log/ vmkernel.log

tail -n 1 -f /var/log/vmkernel.log | grep -m 1 IOMMUIntel >>/var/log/vmkernel.log

This tails the log, one line at a time, grep checks each line for first occurrence, and appends it to the log then tail quits immediately.

这会跟踪日志,一次一行,grep 检查每一行是否第一次出现,并将其附加到日志中,然后 tail 立即退出。

If you like VMware passthough hacking, read more here: http://hackaday.io/project/1071-the-hydra-multiheaded-virtual-computer

如果您喜欢 VMware passthrough hacking,请在此处阅读更多信息:http://hackaday.io/project/1071-the-hydra-multiheaded-virtual-computer