perhaps it could provide some way to reset the error,
although in your case (tail -f), i don't see that there's any particular
benefit in using bufio or the line reader - you could just
read from the end of the file directly.
ReadLine won't help this problem at all - the problem is that
once you've got an error from bufio.Reader (e.g. by reading to the
end of a file) you can't use that bufio.Reader to read any more.
But, this is not the point of this problem. I require a method to
reset the error status after read a EOF to overcome this problem.
On 4月15日, 下午9時19分, peterGo <go.peter...@gmail.com> wrote:
> FYI
>
> bufio: add ReadLinehttp://code.google.com/p/go/source/detail?r=54b2790403aa
Because it doesn't matter where ReadLine lives: it doesn't solve
the problem, yet you replied as though it would. Roger wondered
where his post pointing out the irrelevance of ReadLine had
got lost as a result.
--
Chris "allusive" Dollin
i don't think that's right.
for other kinds of Reader, os.EOF is a permanent thing and
you don't want to call Read again after getting it.
moreover there are other kinds of errors that one might not wish to
persist - for example when using bufio over a network connection
with read timeouts.
ideally bufio could determine whether the error is temporary,
for instance by testing whether the error implements Temporary(),
but that can't work for os.EOF.
i think that providing ClearError might be the best solution here.
if err == os.EOF {
oldstat, err := os.Stat(file)
if err != nil {
log.Fatalln("...")
}
newstat := oldstat
waitforchange:
for ; newstat.Size == oldstat.Size; {
time.Sleep(1e9) // 1 s
newstat, err = os.Stat(file)
if err != nil {
log.Fatalln("Stat")
}
}
if newstat.Size < oldstat.Size {
file.Seek(0,2)
if err != nil {
log.Fatalln("Seek")
}
oldstat = newstat
goto waitforchange
}
l = line.NewReader(bufio.NewReader(file), 80) // keep 10 lines
}
as far as i'm aware, there's no universal substitute for sleep-and-try-again
when waiting for files to change. some platforms provide a notification
mechanism (e.g. os/inotify) but they're not portable and i doubt that
they work on network file systems.
for the particular problem of "implementing unix command 'tail'", i think
a hybrid approach would work best - use bufio to read lines, and then
read the file directly for the polling part (ignoring the fact that the tail
command reads backwards from the end of the file when possible, something
that bufio cannot do).
regardless of that, i still think that ClearError or support for Temporary
errors would be useful for the reasons i outlined above.
for the original poster: here's a version of tail.
// tail prints the last n lines of the file, and
// then polls waiting for more data to be written
// and printing it.
func tail(f *os.File, n int) {
lines := make([]string, n) // circular buffer
i := 0
n = 0
r := bufio.NewReader(f)
w := bufio.NewWriter(os.Stdout)
for {
line, err := r.ReadString('\n')
if len(line) > 0 {
lines[i] = line
i = (i + 1) % len(lines)
if n < len(lines) {
n++
}
}
if err != nil {
break
}
}
for j := (i - n + len(lines)) % len(lines); n > 0; j, n =
(j+1)%len(lines), n-1 {
w.WriteString(lines[j])
}
w.Flush()
buf := make([]byte, 8192)
for {
time.Sleep(1e9)
for {
n, err := f.Read(buf)
if n > 0 {
w.Write(buf[0:n])
}
if err != nil {
break
}
}
w.Flush()
f.Seek(0, 2) // in case the file has been truncated.
}
}
> Go has the powerful *select statement* for channels in its
> repertoire, which seems tailor-made for programming the kind of
> event-driven functionality required for "tail" using a native idiom.
>
> Sleep-and-try-again is most definitely not idiomatic in a language
> that defines goroutines to allow blocked waiting at very low cost as
> a core goal.
If the OS does not provide a source of event notifications, you cannot
use an event-driven approach. You cannot avoid polling. Using
os/inotify for notifications would be nice and idiomatic, but it is
unfortunately Linux-only. There is no portable equivalent.
The language can't change this; it could hide the polling but that
would be unpleasant and complex.
Go implementations already use event-driven I/O operations to make I/O
more efficient under the hood. But it's just an
implementation detail and isn't relevant to the actual language.
> The language is intended to be portable, but that doesn't mean that every
> feature will be supported equally efficiently on all platforms. That could
> never be the case anyway. Platforms differ, machines differ, compilers
> differ, and so do system support libraries. Each platform does the best it
> can. And each of them evolves, there is no fixed point.
An implementation of 'tail' could use os/inotify to wait for a
modification events on the file, but this program would only work on
systems that supported inotify. It depends on the specific
programmer's requirements, it's not a language issue.
- jessta
--
=====================
http://jessta.id.au