Do a tail -F until matching a pattern

2019-01-08 18:30发布

I want to do a tail -F on a file until matching a pattern. I found a way using awk, but IMHO my command is not really clean. The problem is that I need to do it in only one line, because of some limitations.

tail -n +0 -F /tmp/foo | \
awk -W interactive '{if ($1 == "EOF") exit; print} END {system("echo EOF >> /tmp/foo")}'

The tail will block until EOF appears in the file. It works pretty well. The END block is mandatory because awk's "exit" does not exit right away. It makes awk to eval the END block before quitting. The END block hangs on a read call (because of tail), so the last thing I need to do, is to write another line in the file to force tail to exit.

Does someone know a better way to do that?

10条回答
淡お忘
2楼-- · 2019-01-08 19:08

Here's an extended version of Jon's solution which uses sed instead of grep so that the output of tail goes to stdout:

sed -r '/EOF/q' <( exec tail -n +0 -f /tmp/foo ); kill $! 2> /dev/null

This works because sed gets created before tail so $! holds the PID of tail

The main advantage of this over the sh -c solutions is that killing a sh seems to print something to the output such as 'Terminated' which is unwelcome

查看更多
【Aperson】
3楼-- · 2019-01-08 19:13

This is something Tcl is quite good at. If the following is "tail_until.tcl",

#!/usr/bin/env tclsh

proc main {filename pattern} {
    set pipe [open "| tail -n +0 -F $filename"]
    set pid [pid $pipe]
    fileevent $pipe readable [list handler $pipe $pattern]
    vwait ::until_found
    catch {exec kill $pid}
}

proc handler {pipe pattern} {
    if {[gets $pipe line] == -1} {
        if {[eof $pipe]} {
            set ::until_found 1
        }
    } else {
        puts $line
        if {[string first $pattern $line] != -1} {
            set ::until_found 1
        }
    }
}

main {*}$argv

Then you'd do tail_until.tcl /tmp/foo EOF

查看更多
Evening l夕情丶
4楼-- · 2019-01-08 19:13

ready to use for tomcat =

sh -c 'tail -f --pid=$$ catalina.out | { grep -i -m 1 "Server startup in" && kill $$ ;}'

for above scenario :

sh -c 'tail -f   --pid=$$ /tmp/foo | { grep -i -m 1 EOF && kill $$ ;}'
查看更多
成全新的幸福
5楼-- · 2019-01-08 19:15
tail -f <filename> | grep -q "<pattern>"
查看更多
我命由我不由天
6楼-- · 2019-01-08 19:16

I've not results with the solution:

sh -c 'tail -n +0 -f /tmp/foo | { sed "/EOF/ q" && kill $$ ;}'

There is some issue related with the buffer because if there aren't more lines appended to the file, then sed will not read the input. So, with a little more research i came up with this:

sed '/EOF/q' <(tail -n 0 -f /tmp/foo)

The script is in https://gist.github.com/2377029

查看更多
甜甜的少女心
7楼-- · 2019-01-08 19:16
sh -c 'tail -n +0 --pid=$$ -f /tmp/foo | { sed "/EOF/ q" && kill $$ ;}'

Here the main problem is with $$. If you run command as is, $$ is set not to sh but to the PID of the current shell where command is run.

To make kill work you need to change kill $$ to kill \$$

After that you can safely get rid of --pid=$$ passed to tail command.

Summarising, following will work just fine:

/bin/sh -c 'tail -n 0 -f /tmp/foo | { sed "/EOF/ q" && kill \$$ ;}

Optionally you can pass -n to sed to keep it quiet :)

查看更多
登录 后发表回答