26

我想tail -F在一个文件上做一个直到匹配一个模式。我找到了一种使用方法awk,但恕我直言,我的命令并不是很干净。问题是由于一些限制,我只需要在一行中完成。

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

尾部将阻塞,直到EOF出现在文件中。它工作得很好。END 块是强制性的,因为 awkexit不会立即退出。它在退出之前进入 END 块awkevalEND 块挂在读取调用上(因为 tail),所以我需要做的最后一件事是在文件中写另一行以强制 tail 退出。

有人知道更好的方法吗?

4

10 回答 10

42

使用 tail 的--pid选项,当 shell 死亡时 tail 将停止。无需向tail ed 文件添加额外内容。

sh -c 'tail -n +0 --pid=$$ -f /tmp/foo | { sed "/EOF/ q" && kill $$ ;}'
于 2011-02-24T23:53:05.597 回答
35

尝试这个:

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

一旦在/tmp/foo.

有一个副作用:tail进程将一直运行(在后台),直到有任何内容写入/tmp/foo.

于 2011-02-17T19:45:15.437 回答
9

我没有解决方案的结果:

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

有一些与缓冲区相关的问题,因为如果没有更多的行附加到文件中,那么 sed 将不会读取输入。所以,通过更多的研究,我想出了这个:

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

该脚本位于https://gist.github.com/2377029

于 2012-04-13T13:57:15.017 回答
5

这是 Tcl 非常擅长的事情。如果下面是“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

然后你会做tail_until.tcl /tmp/foo EOF

于 2011-02-17T14:21:07.603 回答
4

这对你有用吗?

tail -n +0 -F /tmp/foo | sed '/EOF/q'

我假设“EOF”是您正在寻找的模式。该sed命令在找到它时退出,这意味着它tail应该在下次写入时退出。

tail我想如果在文件的末尾找到模式,就会有一个外部机会存在,等待更多的输出出现在文件中,而这些输出永远不会出现。如果这真的是一个问题,您可能会安排将其杀死 - 整个管道将在终止时sed终止(除非您使用的是一个有趣的外壳,它认为这不是正确的行为)。


对 Bash 不满

正如所担心的那样, (至少在 MacOS X 上,但可能无处不在)是一个认为即使退出bash也需要等待完成的 shell 。有时——比我喜欢的更频繁——我更喜欢旧的 Bourne shell 的行为,它不那么聪明,因此比 Bash 猜错的次数更少。 是一个每秒发送一条消息的程序(示例中为“1:Hello”等),输出到标准输出。在 Bash 中,这个命令序列会一直挂起,直到我在单独的窗口中执行了 ' '。tailseddribblerecho pqr >>/tmp/foo

date
{ timeout -t 2m dribbler -t -m Hello; echo EOF; } >/tmp/foo &
echo Hi
sleep 1   # Ensure /tmp/foo is created
tail -n +0 -F /tmp/foo | sed '/EOF/q'
date

可悲的是,我没有立即看到控制这种行为的选项。我确实找到了shopt lithist,但这与这个问题无关。

Korn Shell 万岁

我注意到,当我使用 Korn shell 运行该脚本时,它会按我的预期工作 - 留下一个tail潜伏的东西以某种方式被杀死。echo pqr >> /tmp/foo在第二个日期命令完成后,那里的工作是 ' '。

于 2011-02-17T02:47:00.733 回答
3

这是 Jon 解决方案的扩展版本,它使用 sed 而不是 grep,以便 tail 的输出进入标准输出:

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

这是有效的,因为 sed 是在 tail 所以 $! 之前创建的。持有tail的PID

与 sh -c 解决方案相比,它的主要优点是杀死 sh 似乎会在输出中打印一些内容,例如不受欢迎的 'Terminated'

于 2011-10-28T18:50:24.037 回答
2
sh -c 'tail -n +0 --pid=$$ -f /tmp/foo | { sed "/EOF/ q" && kill $$ ;}'

这里的主要问题是$$. 如果按原样运行命令,$$则设置不是 sh,而是设置为运行命令的当前 shell 的 PID。

要使杀死工作,您需要更改kill $$kill \$$

之后,您可以安全地摆脱--pid=$$传递给 tail 命令。

总而言之,以下将正常工作:

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

您可以选择传递给以-n保持sed安静:)

于 2011-05-06T02:02:07.527 回答
1

要杀死悬空tail进程,您可以tail在(Bash)进程替换上下文中执行该命令,该命令稍后可以被杀死,就好像它是一个后台进程一样。(代码取自如何通过管道从“tail -f”读取一行,然后终止?)。

: > /tmp/foo
grep -m 1 EOF <( exec tail -f /tmp/foo ); kill $! 2> /dev/null
echo EOF > /tmp/foo  # terminal window 2

作为替代方案,您可以使用命名管道。

(
: > /tmp/foo
rm -f pidfifo
mkfifo pidfifo
sh -c '(tail -n +0 -f /tmp/foo & echo $! > pidfifo) | 
{ sed "/EOF/ q" && kill $(cat pidfifo) && kill $$ ;}'
)

echo EOF > /tmp/foo  # terminal window 2
于 2011-09-18T17:09:06.587 回答
1

准备用于 tomcat =

sh -c 'tail -f --pid=$$ catalina.out | { grep -i -m 1 "服务器启动在" && kill $$ ;}'

对于上述情况:

sh -c 'tail -f   --pid=$$ /tmp/foo | { grep -i -m 1 EOF && kill $$ ;}'
于 2018-02-14T08:52:08.713 回答
-3
tail -f <filename> | grep -q "<pattern>"
于 2015-10-16T10:51:40.007 回答