2015-06-29 9 views
10

ich diese komfortable Funktion in Python hatte:Go: "tail -f" -ähnlichen Generator

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 

Es macht etwas Ähnliches zu UNIX tail -f: Sie letzten Zeilen einer Datei zu erhalten, wie sie kommen. Es ist praktisch, weil Sie den Generator ohne Blockierung erhalten und es an eine andere Funktion übergeben können.

Dann musste ich das gleiche in Go tun. Ich bin neu in dieser Sprache, also bin ich mir nicht sicher, ob das, was ich getan habe, idiomatisch/korrekt genug für Go ist. Hier

ist der 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 
} 

Gibt es eine sauberere Weg, dies in Gehen Sie zu tun? Ich habe das Gefühl, dass ein asynchroner Aufruf für so etwas ein Overkill ist, und es stört mich wirklich.

+2

Verwenden Sie 'os.SEEK_END' anstelle von' 2'. 'time.Sleep (10)' schläft für 10 Nanosekunden, du meinst wahrscheinlich 'time.Sleep (10 * time.Millisecond)'. Sobald ein "io.Reader" Ihnen "io.EOF" gibt, sollten Sie nicht erwarten, dass es Ihnen jemals mehr Daten geben wird; EOF == ** Ende ** der Datei/Steam, keine weiteren Daten. –

+0

Danke, ich habe den Code bearbeitet. – oopcode

Antwort

7

Ich schlage vor, eine Hülle um einen Leser zu schaffen, die auf EOF schläft:

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 
} 

Dieser Leser verwendet werden kann überall ein io.Reader verwendet werden kann. Hier ist, wie eine Schleife über Leitungen mit 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) 
} 

Der Leser kann auch eine Schleife über JSON-Werte an die Datei angefügt werden:

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) 
} 

Es gibt ein paar Vorteile dieser Ansatz über die goroutine hat Ansatz skizziert in der Frage. Die erste ist, dass das Herunterfahren einfach ist. Schließen Sie einfach die Datei. Es besteht keine Notwendigkeit, der Goroutine zu signalisieren, dass sie beendet werden sollte. Der zweite Vorteil ist, dass viele Pakete mit io.Reader arbeiten.

+0

Es gibt zwei Dinge, die ich sehe, sind ein Fehler .... [1] die Zeit.Schlaf (10 * time.Millisecond) wird dazu führen, dass das System Hitze erzeugen, anstatt still zu sitzen, bis es tatsächlich einige Daten gibt. [2] Wenn du am Ende der Datei bist, gibt es kein Signal für die Hauptschleife, so dass du dich entscheiden kannst, das Spiel zu verlassen. (Ich habe einen Anwendungsfall, bei dem ich einige Befehle an einen Dienst sende, dann die Protokolle auf eine positive Antwort wartend; Da es keine negative Antwort gibt, muss ich die Daten erschöpfen oder den Timer überprüfen. Goroutines und Schließen der Datenquelle sind keine Option. – Richard