tailfile.go 1.4 KB

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