這是一個建立於 的文章,其中的資訊可能已經有所發展或是發生改變。tail 是我們大多數人都熟悉的命令。我假設你也熟悉提供的 `-f` 選項。如果你不熟悉,知道它會列印出檔案的最後幾行即可。最近在一個項目上工作,我想知道我需要做什麼來實現這個功能。這個想法來自閱讀 [Feynman](http://amzn.to/2AIWVuX) 的書:> 毫無疑問,你知道如何去做; 但是當你像小孩子一樣玩這類問題,並且你沒有看到答案時...試圖找出如何去做是很有趣的。然後,當你進入成年時,你會培養出一定的自信,你可以去發現事物; 但是如果他們已經被發現,那你根本不應該再來打擾自己。一個傻瓜能做的事,另一個傻瓜也能做,其他一些傻瓜打你的事實不應該打擾你:你應該為將要發現的事物而快樂。實現它可能是一件小事。但我認為這將是一系列文章中的一個良好的開端,在這篇文章中我寫了如何?一些東西。因此,讓我帶你看看如何在 Go 中實現 `tail -f` 的過程。首先,讓我們瞭解這個問題。tail 命令提供了一個標誌,可以“跟蹤”檔案。它所做的是等待添加到檔案末尾的任何更改並將其列印出來。為了簡單起見,我不打算實現 tail,而只是實現跟蹤功能。 所以我們在開始時列印整個檔案,然後在添加它們時列印行。我首先想到的是這樣做的非常幼稚的方式。 列印檔案中的所有位元組,直到達到 `io.EOF`; 讓這個過程睡一會兒,然後再試一次。 我們來看看這個函數:```gofunc follow(file io.Reader) error {r := bufio.NewReader(file)for {by, err := r.ReadBytes('\n')if err != nil && err != io.EOF {return err}fmt.Print(string(by))if err == io.EOF {time.Sleep(time.Second)}}}```當內容寫入檔案時,可悲的是沒有及時做出反應。Linux 提供了一個 API 來監視檔案系統事件:inotify AP。手冊頁給了你一個很好的介紹。它提供了兩個我們感興趣的函數:`inotify_init` 和 `inotify_add_watch`。`inotify_init` 函數建立一個對象,我們將使用該對象進一步與 API 進行互動。`inotify_add_watch` 函數允許你指定感興趣的檔案事件。API 提供了幾個事件,但我們關心的是修改檔案時發出的 `IN_MODIFY` 事件。由於我們使用Go,不得不列出 `syscall` 包。它為前面提到的功能提供了封裝器:`syscall.InotifyInit` 和 `syscall.InotifyAddWatch`。使用 syscall 讓我們看看如何? follow 函數。為了簡潔起見,我省略了錯誤處理,當你看到一個 `_` 變數被使用時,它是處理返回錯誤的好地方。```gofunc follow(filename string) error {file, _ := os.Open(filename)fd, _ := syscall.InotifyInit()_, _ := syscall.InotifyAddWatch(fd, filename, syscall.IN_MODIFY)r := bufio.NewReader(file)for {by, err := r.ReadBytes('\n')if err != nil && err != io.EOF {return err}fmt.Print(string(by))if err != io.EOF {continue}if err = waitForChange(fd); err != nil {return err}}}func waitForChange(fd int) error {for {var buf [syscall.SizeofInotifyEvent]byte_, _ := syscall.Read(fd, buf[:])if err != nil {return err}r := bytes.NewReader(buf[:])var ev = syscall.InotifyEvent{}_ = binary.Read(r, binary.LittleEndian, &ev)if ev.Mask&syscall.IN_MODIFY == syscall.IN_MODIFY {return nil}}}````InotifyInit` 函數返回一個可用於讀取 `sycall.InotifyEvent` 的檔案處理常式。從這個處理常式讀取是一個阻塞操作。 這使我們只有在建立事件時才做出反應。如果您要處理多個作業系統,最好更一般地處理這個作業系統。這就是 fsnotify 軟體包的來源。它提供了一個針對 Linux 的 inotify,BSD 的 kqueue 等的抽象。使用 fsnotify 我們的函數看起來與前面的非常相似,但是更簡單。```gofunc follow(filename string) error {file, _ := os.Open(filename)watcher, _ := fsnotify.NewWatcher()defer watcher.Close()_ = watcher.Add(filename)r := bufio.NewReader(file)for {by, err := r.ReadBytes('\n')if err != nil && err != io.EOF {return err}fmt.Print(string(by))if err != io.EOF {continue}if err = waitForChange(watcher); err != nil {return err}}}func waitForChange(w *fsnotify.Watcher) error {for {select {case event := <-w.Events:if event.Op&fsnotify.Write == fsnotify.Write {return nil}case err := <-w.Errors:return err}}}```我希望代碼解釋得比我寫的文本更好。 為了簡潔,我省略了代碼可能失敗的幾種情況。 為了完善功能,我必須深入挖掘。 但這足以讓我對其產生了知識的渴望:它到底是如何工作的。希望你也由此感覺。
via: http://satran.in/2017/11/15/Implementing_tails_follow_in_go.html
作者:Satyajit Ranjeev 譯者:shniu 校對:polaris1119
本文由 GCTT 原創編譯,Go語言中文網 榮譽推出
本文由 GCTT 原創翻譯,Go語言中文網 首發。也想加入譯者行列,為開源做一些自己的貢獻嗎?歡迎加入 GCTT!
翻譯工作和譯文發表僅用於學習和交流目的,翻譯工作遵照 CC-BY-NC-SA 協議規定,如果我們的工作有侵犯到您的權益,請及時聯絡我們。
歡迎遵照 CC-BY-NC-SA 協議規定 轉載,敬請在本文中標註並保留原文/譯文連結和作者/譯者等資訊。
文章僅代表作者的知識和看法,如有不同觀點,請樓下排隊吐槽
294 次點擊