Go:“tail -f”式发电机 [英] Go: "tail -f"-like generator

查看:127
本文介绍了Go:“tail -f”式发电机的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

  def follow(path):
with open(self。路径)作为行:
lines.seek(0,2)#寻找EOF

而真:
line = lines.readline()
如果不是行:
time.sleep(0.1)
继续
收益行

它的作用类似于UNIX tail -f :你会得到文件最后一行。这很方便,因为你可以在不阻塞的情况下获得生成器并将它传递给另一个函数。

然后我必须在Go中做同样的事情。我是这种语言的新手,所以我不确定我所做的是否符合Go的惯用/正确。



以下是代码:

  func关注(fileName字符串) chan string {
$ b $ out_chan:= make(chan string)

file,err:= os.Open(fileName)
if err!= nil {
log.Fatal(err)
}

file.Seek(0,os.SEEK_END)
bf:= bufio.NewReader(file)

去func(){
用于{
line,_,_:= bf.ReadLine()

如果len(line)== 0 {
time.Sleep(10 * time.Millisecond)
} else {
out_chan< - string(line)
}
}

推迟文件。 Close()
close(out_chan)
}()

return out_chan
}



Go有没有更清晰的方法可以做到这一点?我有一种感觉,对这种事情使用异步调用是一种矫枉过正的行为,它真的让我困扰。

创建一个围绕在EOF上睡觉的阅读器的包装:

 类型t​​ailReader结构{
io.ReadCloser
}

func(t tailReader)Read(b [] byte)(int,error){
for {
n,err:= t.ReadCloser.Read(b)
如果n> 0
return n,nil
} else if err!= io.EOF {
return n,err
}
time.Sleep(10 * time.Millisecond)


$ b $ func newTailReader(fileName string)(tailReader,error){
f,err:= os.Open(fileName)
if err != nil {
return tailReader {},err
}

if _,err:= f.Seek(0,2); err!= nil {
return tailReader {},err
}
return tailReader {f},nil
}

这个阅读器可以在任何可以使用io.Reader的地方使用。以下是使用 bufio.Scanner 循环播放行:

  t,err:= newTailReader(somefile)
if err!= nil {
log.Fatal(err)
}
推迟t.Close()
scanner:= bufio.NewScanner(t)
for scanner.Scan(){
fmt.Println(scanner.Text())
}
if err:= scanner.Err(); err!= nil {
fmt.Fprintln(os.Stderr,reading:,err)
}

阅读器也可用于循环附加到文件的JSON值:

  t, err:= newTailReader(somefile)
if err!= nil {
log.Fatal(err)
}
defer t.Close()
dec: = json.NewDecoder(t)
for {
var v SomeType
if err:= dec.Decode(& v); err!= nil {
log.Fatal(err)
}
fmt.Println(value is,v)
}

这个方法对问题中概述的goroutine方法有几个优点。首先是关机很简单。只需关闭文件。没有必要指示它应该退出的goroutine。第二个优点是许多软件包可以与io.Reader一起使用。

I had this convenient function in Python:

def follow(path):
    with open(self.path) as lines:
        lines.seek(0, 2)  # seek to EOF

        while True:
            line = lines.readline()
            if not line:
                time.sleep(0.1)
                    continue
                yield line 

It does something similar to UNIX tail -f: you get last lines of a file as they come. It's convenient because you can get the generator without blocking and pass it to another function.

Then I had to do the same thing in Go. I'm new to this language, so I'm not sure whether what I did is idiomatic/correct enough for Go.

Here is the code:

func Follow(fileName string) chan string {

    out_chan := make(chan string)

    file, err := os.Open(fileName)
    if err != nil {
        log.Fatal(err)
    }

    file.Seek(0, os.SEEK_END)
    bf := bufio.NewReader(file)

    go func() {
        for {
            line, _, _ := bf.ReadLine()

            if len(line) == 0 {
                time.Sleep(10 * time.Millisecond)
            } else {
                out_chan <- string(line)
            }
        }

        defer file.Close()
        close(out_chan)
    }()

    return out_chan
}

Is there any cleaner way to do this in Go? I have a feeling that using an asynchronous call for such a thing is an overkill, and it really bothers me.

解决方案

I suggest creating a wrapper around a reader that sleeps on EOF:

type tailReader struct {
    io.ReadCloser
}

func (t tailReader) Read(b []byte) (int, error) {
    for {
        n, err := t.ReadCloser.Read(b)
        if n > 0 
            return n, nil
        } else if err != io.EOF {
            return n, err
        }
        time.Sleep(10 * time.Millisecond)
    }
}

func newTailReader(fileName string) (tailReader, error) {
    f, err := os.Open(fileName)
    if err != nil {
        return tailReader{}, err
    }

    if _, err := f.Seek(0, 2); err != nil {
        return tailReader{}, err
    }
    return tailReader{f}, nil
}

This reader can be used anywhere an io.Reader can be used. Here's how loop over lines using bufio.Scanner:

t, err := newTailReader("somefile")
if err != nil {
    log.Fatal(err)
}
defer t.Close()
scanner := bufio.NewScanner(t)
for scanner.Scan() {
    fmt.Println(scanner.Text())
}
if err := scanner.Err(); err != nil {
    fmt.Fprintln(os.Stderr, "reading:", err)
}

The reader can also be used to loop over JSON values appended to the file:

t, err := newTailReader("somefile")
if err != nil {
    log.Fatal(err)
}
defer t.Close()
dec := json.NewDecoder(t)
for {
    var v SomeType
    if err := dec.Decode(&v); err != nil {
       log.Fatal(err)
    }
    fmt.Println("the value is ", v)
}

There are a couple of advantages this approach has over the goroutine approach outlined in the question. The first is that shutdown is easy. Just close the file. There's no need to signal the goroutine that it should exit. The second advantage is that many packages work with io.Reader.

这篇关于Go:“tail -f”式发电机的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆