=Start=
缘由:
想着在学习Go 语言的过程中,尝试把常见命令的功能用Go 语言实现一遍,既加强了Go 语言的学习,又充实了自己的代码仓库,一举两得,何乐而不为?
正文:
参考解答:
大体上来说,要用Go 语言(或是其它的什么编程语言)来实现 tail -f 命令的功能,大致方法和逻辑不外乎:
- 直接调用 tail -f 命令,读取命令的输出;
- 理解 tail -f 命令的原理,照着原理手工实现;
- 调用已有的成熟的第三方库;
第一种调用外部命令的方式就不在这说了,可以参考之前记录的通过os.exec调用系统命令的文章。
第二种是根据 tail -f 命令的原理完全手工实现,个人觉得必要性不大,主要有几点原因:1、原理本身并不复杂,主要是通过定期轮询或是系统底层API的通知机制获知文件内容发生变动,然后从上一次(或第一次)记录的文件偏移offset开始,使用fseek移动到变动内容处调用read/fread进行读取后打印出来;2、虽然原理不复杂,但是要想实现的非常完备也是很困难的,需要考虑各种各样的异常/复杂情况,自己实现性价比不高;3、自己实现的一般不会比成熟的第三方库效率高或是性能好,没有实际使用价值或是实际使用价值很低。
第三种就是调用已有的成熟的第三方库,这里主要是指 github.com/hpcloud/tail 这个。
上面说了一堆,主要是为了引出这个库的使用方法介绍:
package main
import (
"fmt"
"github.com/hpcloud/tail"
"time"
)
func main() {
filename := "/private/tmp/test"
tails, err := tail.TailFile(filename, tail.Config{
ReOpen: true,
Follow: true,
// Location: &tail.SeekInfo{Offset: 0, Whence: 2},
MustExist: false,
Poll: true,
})
if err != nil {
fmt.Println("tail file err:", err)
return
}
var msg *tail.Line
var ok bool
for {
msg, ok = <-tails.Lines
if !ok {
fmt.Printf("tail file close reopen, filename:%s\n", tails.Filename)
time.Sleep(100 * time.Millisecond)
continue
}
fmt.Printf("msg(%T): %v\n", msg, msg)
fmt.Printf("msg(%T): %v\n", msg.Text, msg.Text)
}
}
以上。
参考链接:
- golang基础-tailf日志组件使用
- Reading log files as they’re updated in Go
- How would I read a growing file in Go?
- https://github.com/hpcloud/tail
https://godoc.org/github.com/hpcloud/tail#Line - golang模仿tail命令,显示文件末尾指定行数的文件内容
- https://golang.org/pkg/os/#FileInfo
- https://golang.org/pkg/os/#File.Seek
=END=