# watcher

[![Build Status](https://travis-ci.org/radovskyb/watcher.svg?branch=master)](https://travis-ci.org/radovskyb/watcher)

`watcher` is a Go package for watching for files or directory changes (recursively or non recursively) without using filesystem events, which allows it to work cross platform consistently.

`watcher` watches for changes and notifies over channels either anytime an event or an error has occurred.

Events contain the `os.FileInfo` of the file or directory that the event is based on and the type of event and file or directory path.

[Installation](#installation)  
[Features](#features)  
[Example](#example)  
[Contributing](#contributing)  
[Watcher Command](#command)  

# Update
- Event.OldPath has been added [Aug 17, 2019]
- Added new file filter hooks (Including a built in regexp filtering hook) [Dec 12, 2018]
- Event.Path for Rename and Move events is now returned in the format of `fromPath -> toPath`

#### Chmod event is not supported under windows.

# Installation

```shell
go get -u github.com/radovskyb/watcher/...
```

# Features

- Customizable polling interval.
- Filter Events.
- Watch folders recursively or non-recursively.
- Choose to ignore hidden files.
- Choose to ignore specified files and folders.
- Notifies the `os.FileInfo` of the file that the event is based on. e.g `Name`, `ModTime`, `IsDir`, etc.
- Notifies the full path of the file that the event is based on or the old and new paths if the event was a `Rename` or `Move` event.
- Limit amount of events that can be received per watching cycle.
- List the files being watched.
- Trigger custom events.

# Todo

- Write more tests.
- Write benchmarks.

# Example

```go
package main

import (
	"fmt"
	"log"
	"time"

	"github.com/radovskyb/watcher"
)

func main() {
	w := watcher.New()

	// SetMaxEvents to 1 to allow at most 1 event's to be received
	// on the Event channel per watching cycle.
	//
	// If SetMaxEvents is not set, the default is to send all events.
	w.SetMaxEvents(1)

	// Only notify rename and move events.
	w.FilterOps(watcher.Rename, watcher.Move)

	// Only files that match the regular expression during file listings
	// will be watched.
	r := regexp.MustCompile("^abc$")
	w.AddFilterHook(watcher.RegexFilterHook(r, false))

	go func() {
		for {
			select {
			case event := <-w.Event:	
				fmt.Println(event) // Print the event's info.
			case err := <-w.Error:
				log.Fatalln(err)
			case <-w.Closed:
				return
			}
		}
	}()

	// Watch this folder for changes.
	if err := w.Add("."); err != nil {
		log.Fatalln(err)
	}

	// Watch test_folder recursively for changes.
	if err := w.AddRecursive("../test_folder"); err != nil {
		log.Fatalln(err)
	}

	// Print a list of all of the files and folders currently
	// being watched and their paths.
	for path, f := range w.WatchedFiles() {
		fmt.Printf("%s: %s\n", path, f.Name())
	}

	fmt.Println()

	// Trigger 2 events after watcher started.
	go func() {
		w.Wait()
		w.TriggerEvent(watcher.Create, nil)
		w.TriggerEvent(watcher.Remove, nil)
	}()

	// Start the watching process - it'll check for changes every 100ms.
	if err := w.Start(time.Millisecond * 100); err != nil {
		log.Fatalln(err)
	}
}
```

# Contributing
If you would ike to contribute, simply submit a pull request.

# Command

`watcher` comes with a simple command which is installed when using the `go get` command from above.

# Usage

```
Usage of watcher:
  -cmd string
    	command to run when an event occurs
  -dotfiles
    	watch dot files (default true)
  -ignore string
        comma separated list of paths to ignore
  -interval string
    	watcher poll interval (default "100ms")
  -keepalive
    	keep alive when a cmd returns code != 0
  -list
    	list watched files on start
  -pipe
    	pipe event's info to command's stdin
  -recursive
    	watch folders recursively (default true)
  -startcmd
    	run the command when watcher starts
```

All of the flags are optional and watcher can also be called by itself:
```shell
watcher
```
(watches the current directory recursively for changes and notifies any events that occur.)

A more elaborate example using the `watcher` command:
```shell
watcher -dotfiles=false -recursive=false -cmd="./myscript" main.go ../
```
In this example, `watcher` will ignore dot files and folders and won't watch any of the specified folders recursively. It will also run the script `./myscript` anytime an event occurs while watching `main.go` or any files or folders in the previous directory (`../`).

Using the `pipe` and `cmd` flags together will send the event's info to the command's stdin when changes are detected.

First create a file called `script.py` with the following contents:
```python
import sys

for line in sys.stdin:
	print (line + " - python")
```

Next, start watcher with the `pipe` and `cmd` flags enabled:
```shell
watcher -cmd="python script.py" -pipe=true
```

Now when changes are detected, the event's info will be output from the running python script.
