"tail -f"-类生成器
"tail -f"-like generator
我在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
它的功能类似于 UNIX tail -f
:您可以在文件的最后一行出现时获取它们。这很方便,因为您可以 无阻塞地获取生成器 并将其传递给另一个函数。
然后我不得不在 Go 中做同样的事情。我是这门语言的新手,所以我不确定我所做的 idiomatic/correct 是否足以用于 Go。
代码如下:
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
}
在 Go 中有没有更简洁的方法来做到这一点?我有一种感觉,对这种事情使用异步调用是一种矫枉过正,这真的让我很困扰。
围绕在 EOF 休眠的 reader 创建一个包装器:
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
}
这个 reader 可以用在任何可以使用 io.Reader 的地方。以下是使用 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)
}
reader 也可用于循环附加到文件的 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("the value is ", v)
}
与问题中概述的 goroutine 方法相比,这种方法有几个优点。首先是关机很容易。关闭文件即可。无需向 goroutine 发出它应该退出的信号。第二个优点是许多包都使用 io.Reader.
可以调高或调低睡眠时间以满足特定需求。减少延迟时间并增加时间以减少 CPU 使用。对于向人类显示的数据,100 毫秒的睡眠可能足够快。
查看这个 Go 包以从不断更新的文件中读取 (tail -f):https://github.com/hpcloud/tail
t, err := tail.TailFile("filename", tail.Config{Follow: true})
for line := range t.Lines {
fmt.Println(line.Text)
}
我在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
它的功能类似于 UNIX tail -f
:您可以在文件的最后一行出现时获取它们。这很方便,因为您可以 无阻塞地获取生成器 并将其传递给另一个函数。
然后我不得不在 Go 中做同样的事情。我是这门语言的新手,所以我不确定我所做的 idiomatic/correct 是否足以用于 Go。
代码如下:
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
}
在 Go 中有没有更简洁的方法来做到这一点?我有一种感觉,对这种事情使用异步调用是一种矫枉过正,这真的让我很困扰。
围绕在 EOF 休眠的 reader 创建一个包装器:
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
}
这个 reader 可以用在任何可以使用 io.Reader 的地方。以下是使用 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)
}
reader 也可用于循环附加到文件的 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("the value is ", v)
}
与问题中概述的 goroutine 方法相比,这种方法有几个优点。首先是关机很容易。关闭文件即可。无需向 goroutine 发出它应该退出的信号。第二个优点是许多包都使用 io.Reader.
可以调高或调低睡眠时间以满足特定需求。减少延迟时间并增加时间以减少 CPU 使用。对于向人类显示的数据,100 毫秒的睡眠可能足够快。
查看这个 Go 包以从不断更新的文件中读取 (tail -f):https://github.com/hpcloud/tail
t, err := tail.TailFile("filename", tail.Config{Follow: true})
for line := range t.Lines {
fmt.Println(line.Text)
}