├── README.md ├── bin ├── fwatcher-linux-386 └── fwatcher-linux-amd64 └── fwatcher.go /README.md: -------------------------------------------------------------------------------- 1 | # 程序介绍 2 | go版的文件监控程序,只支持linux系统,提供inotify的底层的原始事件监控,使用时可以获取完全自定义事件,事件发生的时候调用你的自定义命令实现业务操作.支持递归监控,只需要设置要监控的顶级目录,里面的子目录会自动加入监控,而且支持监控目录中动态生成的子目录.   3 | # 文件说明 4 | fwatcher.go是源文件 5 | bin/fwatcher-linux-amd64 是预编译的Linux64位程序 6 | bin/fwatcher-linux-386 是预编译的Linux32位程序 7 | # 源代码使用 8 | 依赖类库: 9 | github.com/tywkeene/go-fsevents 10 | golang.org/x/sys/unix 11 | 1.首先下载源代码 12 | mkdir go-fwatcher 13 | cd go-fwatcher 14 | git clone https://github.com/snail007/go-fwatcher.git . 15 | 2.安装依赖包 16 | go get ./... 17 | 3.然后就可以正常修改源代码开发了 18 | 由于墙的原因如果有些包下载失败,可以到这里手动下载解压到$GOPATH/src下面对应的地方, 19 | golang包下载助手:http://golangtc.com/download/package   20 | 21 | # 编译 22 | 1、编译64位程序 23 | GOOS=linux GOARCH=amd64 go build fwatcher.go 24 | 2、编译32位程序 25 | GOOS=linux GOARCH=386 go build fwatcher.go 26 | 提示: 27 | 编译编译32位也就是386程序的时候,需要注视掉下面这一行: 28 | "IN_ONESHOT":     unix.IN_ONESHOT,     //只监测一次 29 | 30 | # 参数说明 31 |
32 |   -dir 字符串  
33 |     	 监控目录设置,这里填写一个绝对路径即可,程序会自动监控里面的子目录 (default "/tmp")  
34 |   -cmd 字符串  
35 |        设置事件发生的时候执行的命令   
36 |        默认是: "echo %f %t"  
37 |     	 当监控的事件发生时,就执行调用这个参数设置的命令字符串,命令字符串里面可以
38 | 	 使用%f代表发送变化的文件,%t代表发生的事件的flag,有可能是多个事件flag,
39 | 	 意思是这几个事件同时发生,是用逗号分割的,命令可以通过判断%t知道发生的事件
40 | 	 是否是自己需要处理的事件.  
41 |   -events 字符串  
42 |     	 设置想要监听的事件flag;  
43 |          默认是:"IN_ALL_EVENTS,IN_ISDIR,IN_CLOSE,IN_MOVE,IN_EXCL_UNLINK"  
44 |          全部可用的事件flag如下:  
45 |       //基础flag  
46 |       "IN_ACCESS":        unix.IN_ACCESS,        //文件被访问  
47 |       "IN_ATTRIB":        unix.IN_ATTRIB,        //权限,时间戳,UID,GID,其他属性等等,
48 |       						 //link链接的数量 (since Linux 2.6.25)   
49 |       "IN_CLOSE_NOWRITE": unix.IN_CLOSE_NOWRITE, //以非write方式打开文件并关闭  
50 |       "IN_CLOSE_WRITE":   unix.IN_CLOSE_WRITE,   //以write方式打开文件并关闭  
51 |       "IN_CREATE":        unix.IN_CREATE,        //文件或目录被创建  
52 |       "IN_DELETE":        unix.IN_DELETE,        //文件或目录被删除  
53 |       "IN_DELETE_SELF":   unix.IN_DELETE_SELF,   //监控的根目录或文件本身被删除  
54 |       "IN_MODIFY":        unix.IN_MODIFY,        //文件内容被修改  
55 |       "IN_MOVED_FROM":    unix.IN_MOVED_FROM,    //文件移出被监测的目录  
56 |       "IN_MOVED_TO":      unix.IN_MOVED_TO,      //文件移入被监测的目录  
57 |       "IN_MOVE_SELF":     unix.IN_MOVE_SELF,     //监测的根目录或文件本身移动  
58 |       "IN_OPEN":          unix.IN_OPEN,          //文件被打开  
59 |       "IN_CLOEXEC":       unix.IN_CLOEXEC,  
60 |       //集合flag  
61 |       "IN_ALL_EVENTS": unix.IN_ALL_EVENTS, //	以上所有flag的集合"  
62 |       "IN_CLOSE":      unix.IN_CLOSE,      //IN_CLOSE_WRITE | IN_CLOSE_NOWRITE  
63 |       "IN_MOVE":       unix.IN_MOVE,       //IN_MOVED_FROM | IN_MOVED_TO  
64 |       //不常用的flag  
65 |       "IN_DONT_FOLLOW": unix.IN_DONT_FOLLOW, //不follow符号链接 (since 2.6.15)  
66 |       "IN_EXCL_UNLINK": unix.IN_EXCL_UNLINK, //当文件从监测目中unlink后,
67 |       					     //则不再报告该文件的相关event,
68 | 					     //比如监控/tmp使用 (since 2.6.36)  
69 |       "IN_MASK_ADD":    unix.IN_MASK_ADD,    //追加MASK到被监测的pathname    
70 |       "IN_ONESHOT":     unix.IN_ONESHOT,     //只监测一次  
71 |       "IN_ONLYDIR":     unix.IN_ONLYDIR,     //只监测目录  
72 |       //仅由read返回  
73 |       "IN_IGNORED":    unix.IN_IGNORED,    //inotify_rm_watch,文件被删除或者文件系统被umount  
74 |       "IN_ISDIR":      unix.IN_ISDIR,      //发生事件的是一个目录  
75 |       "IN_Q_OVERFLOW": unix.IN_Q_OVERFLOW, //Event队列溢出  
76 |       "IN_UNMOUNT":    unix.IN_UNMOUNT,    //文件系统unmount
77 |       
78 | # 提示 79 | 如果你设置了参数-events包含了集合类型flag,IN_CLOSE、IN_MOVE。 80 | 那么当发生IN_CLOSE事件,-cmd参数里面的%t就是:IN_CLOSE_WRITE,IN_CLOSE_NOWRITE,而不是IN_CLOSE。 81 | 那么当发生IN_MOVE事件,-cmd参数里面的%t就是:IN_MOVED_FROM,IN_MOVED_TO,而不是IN_MOVE。 82 | 集合flag的作用是,帮助快速的设置我们要监听的事件flags,事件发生返回的flag也就是-cmd参数里面的%t只会是非集合flag的组合。 83 | 84 | 85 | 86 | 87 | -------------------------------------------------------------------------------- /bin/fwatcher-linux-386: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/snail007/go-fwatcher/c2091008030cc1962df88b55fb4a0e9d5edc9d39/bin/fwatcher-linux-386 -------------------------------------------------------------------------------- /bin/fwatcher-linux-amd64: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/snail007/go-fwatcher/c2091008030cc1962df88b55fb4a0e9d5edc9d39/bin/fwatcher-linux-amd64 -------------------------------------------------------------------------------- /fwatcher.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bytes" 5 | "flag" 6 | "fmt" 7 | "log" 8 | "os" 9 | "os/exec" 10 | "path/filepath" 11 | "strings" 12 | 13 | "github.com/tywkeene/go-fsevents" 14 | "golang.org/x/sys/unix" 15 | ) 16 | 17 | var dirptr, commandptr, eventsptr *string 18 | var rootDir string 19 | var inotifyFlags int 20 | var options *fsevents.WatcherOptions 21 | var eventsArr []string 22 | 23 | var types = map[string]int{ 24 | //基础flag 25 | "IN_ACCESS": unix.IN_ACCESS, //文件被访问 26 | "IN_ATTRIB": unix.IN_ATTRIB, //文件属性发生变化, e.g., permissions, timestamps, extended attributes, link count (since Linux 2.6.25), UID, GID, etc. (*). 27 | "IN_CLOSE_NOWRITE": unix.IN_CLOSE_NOWRITE, //以非write方式打开文件并关闭 28 | "IN_CLOSE_WRITE": unix.IN_CLOSE_WRITE, //以write方式打开文件并关闭 29 | "IN_CREATE": unix.IN_CREATE, //文件或目录被创建 30 | "IN_DELETE": unix.IN_DELETE, //文件或目录被删除 31 | "IN_DELETE_SELF": unix.IN_DELETE_SELF, //监控的根目录或文件本身被删除 32 | "IN_MODIFY": unix.IN_MODIFY, //文件内容被修改 33 | "IN_MOVED_FROM": unix.IN_MOVED_FROM, //文件移出被监测的目录 34 | "IN_MOVED_TO": unix.IN_MOVED_TO, //文件移入被监测的目录 35 | "IN_MOVE_SELF": unix.IN_MOVE_SELF, //监测的根目录或文件本身移动 36 | "IN_OPEN": unix.IN_OPEN, //文件被打开 37 | //集合flag 38 | "IN_ALL_EVENTS": unix.IN_ALL_EVENTS, // 以上所有flag的集合" 39 | "IN_CLOSE": unix.IN_CLOSE, //IN_CLOSE_WRITE | IN_CLOSE_NOWRITE 40 | "IN_MOVE": unix.IN_MOVE, //IN_MOVED_FROM | IN_MOVED_TO 41 | //不常用的flag 42 | "IN_DONT_FOLLOW": unix.IN_DONT_FOLLOW, //不follow符号链接 (since 2.6.15) 43 | "IN_EXCL_UNLINK": unix.IN_EXCL_UNLINK, //当文件从监测目中unlink后,则不再报告该文件的相关event,比如监控/tmp使用 (since 2.6.36) 44 | "IN_MASK_ADD": unix.IN_MASK_ADD, //追加MASK到被监测的pathname 45 | "IN_ONESHOT": unix.IN_ONESHOT, //只监测一次 46 | "IN_ONLYDIR": unix.IN_ONLYDIR, //只监测目录 47 | //仅由read返回 48 | "IN_IGNORED": unix.IN_IGNORED, //inotify_rm_watch,文件被删除或者文件系统被umount 49 | "IN_ISDIR": unix.IN_ISDIR, //发生事件的是一个目录 50 | "IN_Q_OVERFLOW": unix.IN_Q_OVERFLOW, //Event队列溢出 51 | "IN_UNMOUNT": unix.IN_UNMOUNT, //文件系统unmount 52 | } 53 | 54 | const eventsUsageText = `//基础flag 55 | "IN_ACCESS": unix.IN_ACCESS, //文件被访问 56 | "IN_ATTRIB": unix.IN_ATTRIB, //权限,时间戳,UID,GID,其他属性等等,link链接的数量 (since Linux 2.6.25) 57 | "IN_CLOSE_NOWRITE": unix.IN_CLOSE_NOWRITE, //以非write方式打开文件并关闭 58 | "IN_CLOSE_WRITE": unix.IN_CLOSE_WRITE, //以write方式打开文件并关闭 59 | "IN_CREATE": unix.IN_CREATE, //文件或目录被创建 60 | "IN_DELETE": unix.IN_DELETE, //文件或目录被删除 61 | "IN_DELETE_SELF": unix.IN_DELETE_SELF, //监控的根目录或文件本身被删除 62 | "IN_MODIFY": unix.IN_MODIFY, //文件内容被修改 63 | "IN_MOVED_FROM": unix.IN_MOVED_FROM, //文件移出被监测的目录 64 | "IN_MOVED_TO": unix.IN_MOVED_TO, //文件移入被监测的目录 65 | "IN_MOVE_SELF": unix.IN_MOVE_SELF, //监测的根目录或文件本身移动 66 | "IN_OPEN": unix.IN_OPEN, //文件被打开 67 | //集合flag 68 | "IN_ALL_EVENTS": unix.IN_ALL_EVENTS, // 以上所有flag的集合" 69 | "IN_CLOSE": unix.IN_CLOSE, //IN_CLOSE_WRITE | IN_CLOSE_NOWRITE 70 | "IN_MOVE": unix.IN_MOVE, //IN_MOVED_FROM | IN_MOVED_TO 71 | //不常用的flag 72 | "IN_DONT_FOLLOW": unix.IN_DONT_FOLLOW, //不follow符号链接 (since 2.6.15) 73 | "IN_EXCL_UNLINK": unix.IN_EXCL_UNLINK, //当文件从监测目中unlink后,则不再报告该文件的相关event,比如监控/tmp使用 (since 2.6.36) 74 | "IN_MASK_ADD": unix.IN_MASK_ADD, //追加MASK到被监测的pathname 75 | "IN_ONESHOT": unix.IN_ONESHOT, //只监测一次 76 | "IN_ONLYDIR": unix.IN_ONLYDIR, //只监测目录 77 | //仅由read返回 78 | "IN_IGNORED": unix.IN_IGNORED, //inotify_rm_watch,文件被删除或者文件系统被umount 79 | "IN_ISDIR": unix.IN_ISDIR, //发生事件的是一个目录 80 | "IN_Q_OVERFLOW": unix.IN_Q_OVERFLOW, //Event队列溢出 81 | "IN_UNMOUNT": unix.IN_UNMOUNT, //文件系统unmount` 82 | 83 | var watcherMap = map[string]*fsevents.Watcher{} 84 | 85 | func main() { 86 | dirptr = flag.String("dir", "/tmp", "directory to watch") 87 | eventsptr = flag.String("events", `IN_ALL_EVENTS,IN_ISDIR,IN_CLOSE,IN_MOVE,IN_EXCL_UNLINK`, 88 | `设置想要监听的事件flag; 89 | 默认是:"IN_ALL_EVENTS,IN_ISDIR,IN_CLOSE,IN_MOVE,IN_EXCL_UNLINK" 90 | 全部可用的事件flag如下: 91 | `+eventsUsageText) 92 | commandptr = flag.String("cmd", "echo %f %t", "command to execute on change") 93 | flag.Parse() 94 | rootDir, _ = filepath.Abs(*dirptr) 95 | eventsArr = strings.FieldsFunc(*eventsptr, func(c rune) bool { return c == ',' }) 96 | options = &fsevents.WatcherOptions{ 97 | Recursive: true, 98 | UseWatcherFlags: true, 99 | } 100 | inotifyFlags := (unix.IN_ALL_EVENTS | unix.IN_ISDIR | 101 | unix.IN_CLOSE | unix.IN_MOVE | unix.IN_EXCL_UNLINK) 102 | 103 | w, err := fsevents.NewWatcher(rootDir, inotifyFlags, options) 104 | if err != nil { 105 | panic(err) 106 | } 107 | 108 | log.Printf("Watched Dir : %v", *dirptr) 109 | log.Printf("Event Flags : %v", *eventsptr) 110 | log.Printf("Event Command : %v", *commandptr) 111 | log.Println("Waiting for events...") 112 | handleEvents(w) 113 | 114 | } 115 | 116 | func writeOutput(buf *bytes.Buffer) { 117 | if buf == nil { 118 | return 119 | } 120 | 121 | if len(buf.Bytes()) > 0 { 122 | fmt.Printf("%s\n", string(buf.Bytes())) 123 | } 124 | } 125 | func handleEvents(watcher *fsevents.Watcher) { 126 | watcher.StartAll() 127 | go watcher.Watch() 128 | for { 129 | select { 130 | case event := <-watcher.Events: 131 | if event.RawEvent.Len == 0 { 132 | continue 133 | } 134 | var eventTypesStr = getEventType(*event) 135 | eventTypesArray := strings.FieldsFunc(eventTypesStr, func(c rune) bool { return c == ',' }) 136 | var inarray = true 137 | hasInAllEvents, _ := inArray("IN_ALL_EVENTS", eventsArr) 138 | for _, v := range eventTypesArray { 139 | b, _ := inArray(v, eventsArr) 140 | if !b && hasInAllEvents { 141 | b = ((types[v] & int(types["IN_ALL_EVENTS"])) == types[v]) 142 | } 143 | //log.Println(b, v, eventsArr) 144 | inarray = inarray && b 145 | } 146 | 147 | if event.IsDirCreated() { 148 | w, _ := fsevents.NewWatcher(event.Path, inotifyFlags, options) 149 | watcherMap[event.Path] = w 150 | log.Println("new dir watcher added : " + event.Path) 151 | go handleEvents(w) 152 | } else if event.IsDirRemoved() { 153 | _, ok := watcherMap[event.Path] 154 | if ok { 155 | log.Println("new dir watcher removed : " + event.Path) 156 | watcherMap[event.Path].StopAll() 157 | delete(watcherMap, event.Path) 158 | } 159 | } 160 | 161 | log.Printf("%s [%s]", event.Path, eventTypesStr) 162 | if inarray { 163 | commandStr := strings.Replace(*commandptr, "%f", event.Path, 1) 164 | commandStr = strings.Replace(commandStr, "%t", eventTypesStr, 1) 165 | log.Println("Exec:" + commandStr) 166 | parts := strings.Fields(commandStr) 167 | cmd := exec.Command(parts[0], parts[1:]...) 168 | stdout := &bytes.Buffer{} 169 | stderr := &bytes.Buffer{} 170 | // write stdout to buffer 171 | cmd.Stdout = stdout 172 | cmd.Stderr = stderr 173 | err := cmd.Run() 174 | if err != nil { 175 | os.Stderr.WriteString(fmt.Sprintf("%s\n", err.Error())) 176 | } 177 | // write output 178 | writeOutput(stdout) 179 | writeOutput(stderr) 180 | } 181 | 182 | break 183 | case err := <-watcher.Errors: 184 | log.Println(err) 185 | break 186 | } 187 | } 188 | } 189 | func getEventType(e fsevents.FsEvent) string { 190 | var events []string 191 | var mask = e.RawEvent.Mask 192 | for key, value := range types { 193 | if (value & int(mask)) == value { 194 | events = append(events, key) 195 | } 196 | } 197 | if len(events) == 0 { 198 | events[0] = fmt.Sprintf("%d", e.RawEvent.Mask) 199 | } 200 | return strings.Join(events, ",") 201 | } 202 | func inArray(val string, array []string) (exists bool, index int) { 203 | exists = false 204 | index = -1 205 | 206 | for i, v := range array { 207 | if val == v { 208 | index = i 209 | exists = true 210 | return 211 | } 212 | } 213 | return 214 | } 215 | --------------------------------------------------------------------------------