[英]Do a tail -F until matching a pattern
我想在文件上做一個尾部-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塊在讀取調用時掛起(因為尾部),所以我需要做的最后一件事就是在文件中寫入另一行來強制尾部退出。
有人知道更好的方法嗎?
使用tail的--pid選項,當shell死亡時,tail將停止。 無需在tailed文件中添加額外內容。
sh -c 'tail -n +0 --pid=$$ -f /tmp/foo | { sed "/EOF/ q" && kill $$ ;}'
嘗試這個:
sh -c 'tail -n +0 -f /tmp/foo | { sed "/EOF/ q" && kill $$ ;}'
只要在/ tmp / foo中看到“EOF”字符串,整個命令行就會退出。
有一個副作用:尾部進程將保持運行(在后台),直到將任何內容寫入/ tmp / foo。
我沒有解決方案的結果:
sh -c 'tail -n +0 -f /tmp/foo | { sed "/EOF/ q" && kill $$ ;}'
存在與緩沖區相關的一些問題,因為如果沒有更多行附加到文件,則sed將不會讀取輸入。 所以,通過更多的研究我想出了這個:
sed '/EOF/q' <(tail -n 0 -f /tmp/foo)
這對你有用嗎?
tail -n +0 -F /tmp/foo | sed '/EOF/q'
我假設'EOF'是你正在尋找的模式。 sed
命令在找到它時退出,這意味着tail
應該在下次寫入時退出。
我想如果在文件的末尾找到模式,等待更多的輸出出現在文件中,這將永遠不會出現,那么tail
可能會出現問題。 如果這真的是一個問題,你可能會安排殺死它 - 整個管道將在sed
終止時終止(除非你使用一個有趣的shell來決定這是不正確的行為)。
正如所擔心的那樣, bash
(至少在MacOS X上,但可能在任何地方)是一個shell,它認為即使sed
退出也需要等待tail
完成。 有時 - 比我更喜歡 - 我更喜歡老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運行該腳本時,它會像我期望的那樣工作 - 留下潛伏的tail
以某種方式被殺死。 在第二個date命令完成后,有什么效果' echo pqr >> /tmp/foo
'。
這是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
sh -c 'tail -n +0 --pid=$$ -f /tmp/foo | { sed "/EOF/ q" && kill $$ ;}'
這里的主要問題是$$
。 如果按原樣運行命令,則$$
設置為不是sh,而是設置為運行命令的當前shell的PID。
要使kill工作,你需要將kill $$
更改為kill \\$$
之后你可以安全地擺脫傳遞給tail命令的--pid=$$
。
總結一下,以下工作會很好:
/bin/sh -c 'tail -n 0 -f /tmp/foo | { sed "/EOF/ q" && kill \$$ ;}
您可以選擇將-n
傳遞給sed
以保持安靜:)
這是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',這是不受歡迎的
准備用於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 $$ ;}'
為了殺死懸空tail
過程,您可以在(Bash)過程替換上下文中執行tail
命令,該過程隨后可以被殺死,就好像它是一個后台進程一樣。 (代碼取自如何從'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
tail -f <filename> | grep -q "<pattern>"
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.