tailfile.go 1.2 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162
  1. package tailfile
  2. import (
  3. "bytes"
  4. "errors"
  5. "io"
  6. "os"
  7. )
  8. const blockSize = 1024
  9. var eol = []byte("\n")
  10. var ErrNonPositiveLinesNumber = errors.New("Lines number must be positive")
  11. //TailFile returns last n lines of file f
  12. func TailFile(f io.ReadSeeker, n int) ([][]byte, error) {
  13. if n <= 0 {
  14. return nil, ErrNonPositiveLinesNumber
  15. }
  16. size, err := f.Seek(0, os.SEEK_END)
  17. if err != nil {
  18. return nil, err
  19. }
  20. block := -1
  21. var data []byte
  22. var cnt int
  23. for {
  24. var b []byte
  25. step := int64(block * blockSize)
  26. left := size + step // how many bytes to beginning
  27. if left < 0 {
  28. if _, err := f.Seek(0, os.SEEK_SET); err != nil {
  29. return nil, err
  30. }
  31. b = make([]byte, blockSize+left)
  32. if _, err := f.Read(b); err != nil {
  33. return nil, err
  34. }
  35. data = append(b, data...)
  36. break
  37. } else {
  38. b = make([]byte, blockSize)
  39. if _, err := f.Seek(step, os.SEEK_END); err != nil {
  40. return nil, err
  41. }
  42. if _, err := f.Read(b); err != nil {
  43. return nil, err
  44. }
  45. data = append(b, data...)
  46. }
  47. cnt += bytes.Count(b, eol)
  48. if cnt > n {
  49. break
  50. }
  51. block--
  52. }
  53. lines := bytes.Split(data, eol)
  54. if n < len(lines) {
  55. return lines[len(lines)-n-1 : len(lines)-1], nil
  56. }
  57. return lines[:len(lines)-1], nil
  58. }