做尾巴-F直到匹配模式

时间:2011-02-17 02:06:52

标签: shell sed awk tcl 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块是强制性的,因为awk的“退出”不会立即退出。在退出之前使awk评估END块。 END块在读取调用时挂起(因为尾部),所以我需要做的最后一件事就是在文件中写入另一行来强制尾部退出。

有人知道更好的方法吗?

10 个答案:

答案 0 :(得分:32)

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

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

答案 1 :(得分:29)

试试这个:

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

只要在/ tmp / foo中看到“EOF”字符串,整个命令行就会退出。

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

答案 2 :(得分:8)

我的解决方案没有结果:

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

答案 3 :(得分:4)

这对你有用吗?

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

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

我认为,如果在文件末尾找到模式,等待更多输出出现在文件中,那么tail就会出现这种情况。如果这真的是一个问题,你可能会安排杀死它 - 整个管道将在sed终止时终止(除非你使用一个有趣的shell来决定这是不正确的行为)。


Grump关于Bash

正如所担心的那样,bash(至少在MacOS X上,但可能在任何地方)是一个shell,它认为即使tail退出,也需要等待sed完成。有时 - 比我喜欢的更多 - 我更喜欢老Bourne shell的行为,这种行为不是那么聪明,因此猜错的频率低于Bash。 dribbler是一个每秒输出一次消息的程序(示例中为'1:Hello'等),输出转到标准输出。在Bash中,此命令序列一直挂起,直到我在一个单独的窗口中执行“echo 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的Hooray

我注意到,当我使用Korn shell运行该脚本时,它会像我期望的那样工作 - 让tail潜伏在某处以便以某种方式被杀死。在第二个日期命令完成后,“echo pqr >> /tmp/foo”的作用是什么。

答案 4 :(得分:4)

这是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

答案 5 :(得分:2)

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

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

要进行杀戮工作,您需要将kill $$更改为kill \$$

之后你可以安全地摆脱传递给tail命令的--pid=$$

总结一下,以下工作会很好:

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

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

答案 6 :(得分:2)

这是Jon的解决方案的扩展版本,它使用sed而不是grep,以便tail的输出转到stdout:

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

这是有效的,因为sed会在尾部之前创建,所以$!持有尾巴的PID

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

答案 7 :(得分:1)

要杀死悬空tail进程,您也可以在(Bash)进程替换上下文中执行tail命令,以后可以将其作为后台进程杀死。 (代码取自How to read one line from 'tail -f' through a pipeline, and then terminate?)。

: > /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

答案 8 :(得分:1)

准备用于tomcat =

sh -c'tail -f --pid = $$ catalina.out | {grep -i -m 1“服务器启动”&amp;&amp;杀死$$;}'

对于上述情况:

sh -c 'tail -f   --pid=$$ /tmp/foo | { grep -i -m 1 EOF && kill $$ ;}'

答案 9 :(得分:-2)

tail -f <filename> | grep -q "<pattern>"