简介

Watcher 是一个可以实时观测某个文件或文件夹变动的 Golang 包,在不使用文件系统事件的情况下。所以利用这个包可以使我们的跨平台工作更加连贯。

Example

官方示例

  1. package main
  2. import (
  3. "fmt"
  4. "log"
  5. "time"
  6. "github.com/radovskyb/watcher"
  7. )
  8. func main() {
  9. w := watcher.New()
  10. w.SetMaxEvents(1)
  11. w.FilterOps(watcher.Rename, watcher.Move)
  12. go func() {
  13. for {
  14. select {
  15. case event := <-w.Event:
  16. fmt.Println(event)
  17. case err := <-w.Error:
  18. log.Fatalln(err)
  19. case <-w.Closed:
  20. return
  21. }
  22. }
  23. }()
  24. if err := w.Add("."); err != nil {
  25. log.Fatalln(err)
  26. }
  27. if err := w.AddRecursive("../test_folder"); err != nil {
  28. log.Fatalln(err)
  29. }
  30. for path, f := range w.WatchedFiles() {
  31. fmt.Printf("%s: %s\n", path, f.Name())
  32. }
  33. fmt.Println()
  34. go func() {
  35. w.Wait()
  36. w.TriggerEvent(watcher.Create, nil)
  37. w.TriggerEvent(watcher.Remove, nil)
  38. }()
  39. if err := w.Start(time.Millisecond * 100); err != nil {
  40. log.Fatalln(err)
  41. }
  42. }

运行结果

Watcher - 图1

官方示例(Command)

  1. package main
  2. import (
  3. "flag"
  4. "fmt"
  5. "log"
  6. "os"
  7. "os/exec"
  8. "os/signal"
  9. "strings"
  10. "time"
  11. "unicode"
  12. "github.com/radovskyb/watcher"
  13. )
  14. func main() {
  15. interval := flag.String("interval", "100ms", "watcher poll interval")
  16. recursive := flag.Bool("recursive", true, "watch folders recursively")
  17. dotfiles := flag.Bool("dotfiles", true, "watch dot files")
  18. cmd := flag.String("cmd", "", "command to run when an event occurs")
  19. startcmd := flag.Bool("startcmd", false, "run the command when watcher starts")
  20. listFiles := flag.Bool("list", false, "list watched files on start")
  21. stdinPipe := flag.Bool("pipe", false, "pipe event's info to command's stdin")
  22. keepalive := flag.Bool("keepalive", false, "keep alive when a cmd returns code != 0")
  23. flag.Parse()
  24. files := flag.Args()
  25. if len(files) == 0 {
  26. curDir, err := os.Getwd()
  27. if err != nil {
  28. log.Fatalln(err)
  29. }
  30. files = append(files, curDir)
  31. }
  32. var cmdName string
  33. var cmdArgs []string
  34. if *cmd != "" {
  35. split := strings.FieldsFunc(*cmd, unicode.IsSpace)
  36. cmdName = split[0]
  37. if len(split) > 1 {
  38. cmdArgs = split[1:]
  39. }
  40. }
  41. w := watcher.New()
  42. w.IgnoreHiddenFiles(*dotfiles)
  43. done := make(chan struct{})
  44. go func() {
  45. defer close(done)
  46. for {
  47. select {
  48. case event := <-w.Event:
  49. fmt.Println(event)
  50. if *cmd != "" {
  51. c := exec.Command(cmdName, cmdArgs...)
  52. if *stdinPipe {
  53. c.Stdin = strings.NewReader(event.String())
  54. } else {
  55. c.Stdin = os.Stdin
  56. }
  57. c.Stdout = os.Stdout
  58. c.Stderr = os.Stderr
  59. if err := c.Run(); err != nil {
  60. if !c.ProcessState.Success() && *keepalive {
  61. log.Println(err)
  62. continue
  63. }
  64. log.Fatalln(err)
  65. }
  66. }
  67. case err := <-w.Error:
  68. if err == watcher.ErrWatchedFileDeleted {
  69. fmt.Println(err)
  70. continue
  71. }
  72. log.Fatalln(err)
  73. case <-w.Closed:
  74. return
  75. }
  76. }
  77. }()
  78. for _, file := range files {
  79. if *recursive {
  80. if err := w.AddRecursive(file); err != nil {
  81. log.Fatalln(err)
  82. }
  83. } else {
  84. if err := w.Add(file); err != nil {
  85. log.Fatalln(err)
  86. }
  87. }
  88. }
  89. if *listFiles {
  90. for path, f := range w.WatchedFiles() {
  91. fmt.Printf("%s: %s\n", path, f.Name())
  92. }
  93. fmt.Println()
  94. }
  95. fmt.Printf("Watching %d files\n", len(w.WatchedFiles()))
  96. parsedInterval, err := time.ParseDuration(*interval)
  97. if err != nil {
  98. log.Fatalln(err)
  99. }
  100. closed := make(chan struct{})
  101. c := make(chan os.Signal)
  102. signal.Notify(c, os.Kill, os.Interrupt)
  103. go func() {
  104. <-c
  105. w.Close()
  106. <-done
  107. fmt.Println("watcher closed")
  108. close(closed)
  109. }()
  110. go func() {
  111. if *cmd != "" && *startcmd {
  112. com := exec.Command(cmdName, cmdArgs...)
  113. com.Stdin = os.Stdin
  114. com.Stdout = os.Stdout
  115. com.Stderr = os.Stderr
  116. if err := com.Run(); err != nil {
  117. log.Fatalln(err)
  118. }
  119. }
  120. }()
  121. if err := w.Start(parsedInterval); err != nil {
  122. log.Fatalln(err)
  123. }
  124. <-closed
  125. }

运行结果

./watcher
Watcher - 图2
./watcher -list
Watcher - 图3

试试进行文件改动
运行 wacher 后在另一个终端 touch Newfile
Watcher - 图4

不出所料,检测到了变化
再试试删除命令 rm ./Newfile
Watcher - 图5

再试试其他的, touch Newfile | mv Newfile Changed
Watcher - 图6

小结

具体的使用方法在官方文档已经写得很清楚,这里不再赘述,之后我们进行源码分析