[HN Gopher] Watchman: Execute a command when something changes
       ___________________________________________________________________
        
       Watchman: Execute a command when something changes
        
       Author : ColinWright
       Score  : 200 points
       Date   : 2022-10-05 12:50 UTC (10 hours ago)
        
 (HTM) web link (github.com)
 (TXT) w3m dump (github.com)
        
       | majodev wrote:
       | Very happy user of https://github.com/watchexec/watchexec (rust),
       | can recommend.
        
         | maximilianroos wrote:
         | Very much agree. And the maintainer has been super responsive.
        
         | loevborg wrote:
         | Yes, this is the one I reach for always. It works reliably
         | AFAICT, runs on all platforms, is simple enough and still has
         | the necessary features.
        
       | st0le wrote:
       | Is there a similar tool that can trigger a command when the
       | output of command changes or the stdout has some kind of string?
       | 
       | I understand this is shell script territory, but a cross-platform
       | tool would be ideal.
        
       | pletnes wrote:
       | If you like python or work on mac/win/linux, you might like
       | https://github.com/samuelcolvin/watchfiles
       | 
       | Uses rust for performance, and is usable from the terminal or
       | from python.
        
       | amelius wrote:
       | From my experience, these types of notification handlers are very
       | fragile and tend to fail every so often.
       | 
       | Do not use if your life depends on it.
        
         | remram wrote:
         | From my experience, they break _every time_ if you are making
         | changes with tools that replace the file rather than change it
         | in-place, like most text editors.
        
           | Brian_K_White wrote:
           | At least with inotify, ie, incron, this is just a matter of
           | setting a less stupid watch.
           | 
           | You can ask the kernel to watch for several different
           | specific kinds of fs events, either at the file or directory
           | level.
           | 
           | Detecting when a file is either created, or closed from
           | writing, is no problem at all. Ie, handles the case of
           | writing a file normally, editing a file, and renaming a file
           | into exitence without having opened it for writing (that
           | happened while it had some other name you weren't watching).
           | This would handle unlinking and recreating the same as
           | editing or uploading.
           | 
           | You can watch specifically for the close event only, and even
           | more specifically for close-from-write-mode to detect updates
           | but ignore until the update is done. And at the same time you
           | can also watch for creation, which handles creating a file
           | under some other name and then renaming it into existence
           | under the name you care about without the name you care about
           | ever having a close-from-write event.
           | 
           | That would fire off your script when a filename you were
           | watching was unlinked and re-created instead of edited in-
           | place, and then it's up to your script to handle the case
           | where it's a "new" file that is really just replacing an old
           | file.
           | 
           | I don't even see why that should be any kind of special
           | problem. Are some tools doing something like maintaining ooen
           | file handles or something for every watched file??? That
           | would break by unlink-create, but that would be insane.
           | 
           | Perhaps these wrappers that try to simplify things are as
           | usual just a way to break the thing they are trying to
           | simplify. Just misguided crap.
           | 
           | If the feature is baked into an ide or media server and
           | failing to handle that case, well they don't _have_ to fail
           | to handle that case. The subsystem totally supplies the
           | functionality.
           | 
           | For something random adhoc where you're writing your own
           | incrontab and handler script, it's no problem at all.
        
           | diffxx wrote:
           | Yeah, this is difficult, though not impossible, to get right
           | with the inotify/kqueue api. Windows and mac do provide apis
           | for recursive directory monitoring but a cross platform tool
           | will have to solve this problem. At a high level, the way to
           | do it is to create an in memory representation of the file
           | system that caches a watch handle for every file. When a
           | deletion of a file is detected, you must create a watch on
           | the parent directory, if there wasn't one already. Then you
           | should be able to detect the ensuing create. To make this
           | more concrete, the problem that a file watcher needs to solve
           | is the problem of keeping its in memory representation of the
           | file system consistent with the actual file system. Watch
           | events are just a useful side effect of this process.
           | 
           | The other fun part is that there is often a lag between the
           | deletion and the create in the text editor case so it is
           | necessary to defer triggering events when a deletion is
           | detected and wait a little while to see if there is a
           | corresponding creation. Otherwise you may rerun your command
           | that depends on the file before it exists.
           | 
           | It is possible to get like a 99+% solution to this problem
           | without polling but it is a lot harder than what these simple
           | tools, including entr do. The upshot is that file monitoring
           | should be looked at as a lowest common denominator solution.
           | A better solution is to build automatic command running into
           | the text editor itself.
        
           | amelius wrote:
           | In that case, you might want to monitor the directory too,
           | not just the file. I believe this is even how it is supposed
           | to work.
        
             | remram wrote:
             | Yeah, and then filter out the events in the directory to
             | find out when the correct file changed, etc. That is
             | exactly what I would want the tool to abstract for me.
        
       | dabears wrote:
       | Inotify can drop events when there is a burst of file changes.
       | You can bump up the max number of queued events though. For
       | example, if you're watching a git repo and switch to an old
       | branch that has 1500+ changed files then inotify will likely miss
       | some with a default configuration.
        
       | sproketboy wrote:
        
       | stoplying1 wrote:
       | Watchman works great, but the CLI UX is... really kinda dreadful
       | in my opinion. Trying to get it usable in diverse scenarios kinda
       | reveals that the author has a very particular way it's meant to
       | be used and it seems needlessly frustrating to use it otherwise.
        
       | david2ndaccount wrote:
       | I wrote a similar tool for macOS, using libdispatch.
       | 
       | https://github.com/drpriver/macwatch
        
       | benjaminjosephw wrote:
       | This project wraps `inotifywait`[0] which I didn't know about
       | until just now. Really useful utility, thanks for sharing!
       | 
       | [0] - https://www.man7.org/linux/man-
       | pages/man1/inotifywait.1.html
        
       | dec0dedab0de wrote:
       | I'm so used to making snap judgements on complex projects, and
       | seeing that every file was last committed 8 years ago is usually
       | a giant red flag to me.
       | 
       | However, this is a single bash script, which definitely could be
       | stable and forgotten about. Something like this would be more
       | appealing as a posix shell script, but if you're using bash
       | anyway and not trying to distribute, then stable and simple goes
       | a long way.
        
       | e-dant wrote:
       | Those considering doing something like this (such as the author)
       | might consider using my library:
       | 
       | https://github.com/e-dant/watcher
       | 
       | It hooks into system APIs where viable. (Otherwise, it uses
       | std::filesystem.)
       | 
       | It's meant to be as or more:
       | 
       | - Easy to use
       | 
       | - Efficient
       | 
       | Than all similar libraries.
        
       | wmichelin wrote:
       | I have been using reflex https://github.com/cespare/reflex for
       | the same thing in some of our docker projects
        
         | athorax wrote:
         | +1 for reflex, love being able to have a config for development
         | 
         | Example .reflex.conf to run one command when an openapi spec
         | changes and another when any go files change:
         | -g "spec.yaml" -- bash -c 'make'       -sr "\.go$" -- go run
         | cmd/app/main.go
         | 
         | Then run it with:                 reflex -d fancy -c
         | .reflex.conf
        
       | Brian_K_White wrote:
       | incron
        
       | jimpudar wrote:
       | I haven't seen anybody here mention that you can do this with
       | systemd path units [0],[1].
       | 
       | I know not everybody uses Linux (or loves systemd as much as I
       | do) but it's a great solution if you already use systemd.
       | 
       | [0] https://www.redhat.com/sysadmin/introduction-path-units
       | 
       | [1]
       | https://www.freedesktop.org/software/systemd/man/systemd.pat...
        
       | IncRnd wrote:
       | What does this add to inotifywait? That does exactly the same as
       | this project, which uses inotifywait to reimplement inotifywait.
       | 
       | For example:                 dnf install inotify-tools       or
       | apt-get install inotify-tools
        
         | cortesoft wrote:
         | It looks like a simple bash wrapper around inotifywait to
         | provide a simpler interface for a subset of the functionality.
         | You don't have to remember any flags.
         | 
         | Not super useful, but not useless.
        
         | masukomi wrote:
         | ifnotifywait doesn't exist on macOS, so in addition to that not
         | being an option on macos, this would be something you could
         | write tools that a team who used multiple platforms, or a
         | person with setups on multiple os's, could use. I suspect this
         | might even be usable on windows.
         | 
         | [edit: fswatch looks very promising, and may be a better choice
         | than either ifnotifywait or watchman]
        
           | awinder wrote:
           | This wraps inotifywait, so it doesn't really address cross-
           | platform needs.
           | 
           | The unique features in this over straight inotifywait is
           | plugging together the command you want to execute, and some
           | event dedupe logic. Grand majority of the code is around
           | colorized logs, command docs, and some options handling code
           | which mostly just gets piped to inotifywait.
        
           | wwweston wrote:
           | Discovered fswatch recently, it's proved _very_ useful, my
           | favorite use being replacing a few situations where I was
           | using sshfs with fswatch+rsync: no network lag on save /load,
           | can even work offline, but everything still makes its way to
           | the network destination!
        
             | williamcotton wrote:
             | I've been using fswatch for a project that supports both
             | linux and darwin. Here's an example from a Makefile of how
             | I use it to re-run tests on file change:
             | test-watch:         make --no-print-directory test || :
             | fswatch --event Updated -o test/*.c test/*.h src/ | xargs
             | -n1 -I{} make --no-print-directory test
        
             | brigandish wrote:
             | This one? https://github.com/emcrisostomo/fswatch
        
           | IncRnd wrote:
           | You are confused about how the tool works. This project uses
           | inotifywait from inotify tools to reimplement what
           | inotifywait already does. The linked article page at github
           | clearly says this.
           | 
           | Adding a new layer atop inotify tools doesn't make that
           | package automatically run on new platforms. There are already
           | tools on macos that use the native FSEvents API for watching
           | for file changes and executing scripts.
        
         | [deleted]
        
       | EvanAnderson wrote:
       | Does anybody have a good "execute a command when nothing changes"
       | tool? I have some situations where I'd like to take action if,
       | say, a file hasn't had its mtime updated in 24 hours.
        
         | pitaj wrote:
         | Does it need to be precise? I'd probably just use an hourly
         | cron job.
        
         | yabones wrote:
         | Not sure if there's a way to do it instantly, but one way is to
         | just run a find command in a cron job every X hours:
         | find /foo/bar -type f -mtime +1 | xargs foobar
        
       | lloeki wrote:
       | I've been using fswatch to great effect. The big advantage is
       | that it's truly cross-platform, falling back to polling if no fs
       | notification system is available as long as stat(2) works.
       | 
       | Very composable as you just pipe its output to whatever you want
       | (typically a while read do end), so exclusion is a
       | awk/sed/perl/grep/rg away, and command can be as simple or
       | complex as it needs to while the tool itself stays lean.
       | 
       | Cool features include -o to just be notified that something
       | changed (e.g to fire up rsync), and ability to batch changes.
       | 
       | Also the command is a front end to libfswatch, so one can use
       | that directly if shelling is to be foregone.
       | 
       | https://github.com/emcrisostomo/fswatch
        
         | chmaynard wrote:
         | > If you are installing fswatch using a package manager and you
         | would like the PDF manual to be bundled into the package,
         | please send a feature request to the package maintainer.
         | 
         | I can't find the PDF manual in the repo. Do I need to contact
         | the author and say please?
        
       | bin_bash wrote:
       | this project hasn't had a commit in 8 years and only has a
       | handful of stars. It confusingly shares the name with a _much_
       | more popular tool from Meta--though the use case is slightly
       | different.
       | 
       | There are other much more popular tools that do the same thing,
       | like entr, watchexec, and nodemon. Why is this posted?
        
         | [deleted]
        
         | Chris2048 wrote:
         | Funnily enough, the real value of this post is the thread
         | detailing all the alternatives...
        
         | TheRealPomax wrote:
         | Small correction: watchman is a tool made and released by
         | Facebook, not made and released by Meta.
         | 
         | (yes, one owns the other, but Facebook employees writing and
         | releasing code for Facebook are on Facebook's payroll, not
         | Meta's payroll)
        
           | bin_bash wrote:
           | No, there is no such thing as "Facebook's Payroll". It's all
           | Meta. I'm a former Meta employee. Watchman is used heavily
           | across the family of apps. It's not like Alphabet that's a
           | holding company for different smaller companies.
           | 
           | Watchman not yet being branded with "Meta" is a case of
           | laziness. It doesn't indicate it's limited to the Blue app.
        
       | tartuffe78 wrote:
       | I'm gonna try to use this to revert the .xcscheme file changes
       | that Xcode makes when I switch between various Xcode versions on
       | my machine. These happen in 10+ submodules everyday so this is
       | great!
        
       | m00dy wrote:
       | I wrote something very similar.
       | 
       | https://github.com/m00dy/fwe
        
       | akras14 wrote:
       | Edit: it may have been a different watchman, but same point still
       | applies.
       | 
       | It's been a few years, so perhaps it got addressed, but this
       | library used to introduce a "memory leak" by design, by infinity
       | storing pointers to files being watched.
       | 
       | It was a pain to isolate (https://techtldr.com/simple-guide-to-
       | finding-a-javascript-me...)
       | 
       | Ultimately I ended up fixing it by writing my own version using
       | native system calls available on Node.
       | 
       | The moral of the story, this library is great if you are building
       | cross platform dev tool. It is not (at least wasn't at the time)
       | great for long lived processes that target only one type of
       | operating system.
        
       | vinay_ys wrote:
       | Its a simple shell script wrapper on top of inotify-tools. I
       | suppose its biggest contribution is a more appealing name -
       | watchman.
        
       | [deleted]
        
       | VyseofArcadia wrote:
       | I've seen about a dozen versions of this now. So my question is,
       | who watches the watchmen?
        
         | cjcampbell wrote:
         | Came here for this. Thank you.
        
         | koolba wrote:
         | If it's running in a shell, it'd be the terminal session's
         | shell. If it's running as a daemon, in a traditional Unix
         | environment, it'd be the init process (pid 0).
        
           | Brian_K_White wrote:
           | Inotify is a kernel feature.
           | 
           | Neither init or a shell or any other userspace code. The
           | userspace code like in inotifywait or incron just tells the
           | kernel what it wants the kernel to watch, and then exits,
           | gone, no further parent, not even init.
           | 
           | Ok now that I mentioned incron..., incron being a daemon is
           | managed by init, but incron is not watching any files, not
           | even it's own config files. incron just reads it's incrontab
           | files and registers the desired events with the kernel, and
           | then performs the desired actions when the kernel triggers
           | it. It notices when it's own config files change the same way
           | as any other inotify job, it asks the kernel to tell it when
           | they change.
           | 
           | incron is like cron where you write a crontab-like file that
           | specifies a command of your choosing. Pretty much the same as
           | this thing minus some colorful display.
           | 
           | I guess the point of this thing would be if it provides a
           | consistent wrapper interface over the various fs watching
           | mechanisms on different OS's.
           | 
           | But for linux, I've already been using incron to do this job
           | efficiently for ages.
        
       | [deleted]
        
       | benniomars wrote:
       | Nice. I've been using yardman that's written for NodeJS and Crow
       | written in Go. Nice to have something that's just bash.
        
       | pyuser583 wrote:
       | Very cool
        
       | BrainVirus wrote:
       | This command seems like a no-brainer that should be in all OSes
       | by default.
       | 
       | It's mind-boggling that most systems don't have a standard tool
       | for observing file changes. The only language I know of that has
       | a platform-agnostic API for this is Factor.
        
         | dspillett wrote:
         | The required kernel hooks exist in pretty much any common OS
         | these days, it is a user-space tool that is sometimes missing.
         | 
         | It may not be installed by default, but inotifywait is
         | available in common Linux distributions, usually in a package
         | called something like ionotify-tools, and has been for over a
         | decade-ana-half IIRC. It'll work under WSL on Windows too,
         | though only for ext4 devices not bits of the Windows filesystem
         | made available to Linux.
         | 
         | I can't speak to what other OSs include by default, but as
         | every major OS has a different API for how to register a
         | listener & how it gets messages no built-in tool is going to be
         | cross-platform. There are third party tools which present more
         | cross-platform consistency, most notably
         | https://github.com/emcrisostomo/fswatch#readme (also available
         | in common Linux distros, just an _apt install_ away in Debian
         | for instance).
        
       | jitl wrote:
       | Not to be confused with Facebook's file watch daemon, also names
       | watchman, which does the same sort of thing but is more
       | complicated. There's a bunch of tools that integrate Facebook's
       | watchman for more efficient change tracking.
       | 
       | Advantages of Facebook's watchman:
       | 
       | 1. Implements efficient file system event watches on macOS and
       | Windows
       | 
       | 2. IPC/daemon system reduces resource use because overlapping
       | watches/triggers don't use more inotify slots.
       | 
       | 3. Denounces / waits for changes to settle
       | 
       | 4. Client libraries in a few different languages for scripting
       | purposes
       | 
       | https://facebook.github.io/watchman/
        
         | [deleted]
        
         | malkia wrote:
         | Also support for other OS's - like Windows.
        
           | giantg2 wrote:
           | I have just used some Power Shell scripts for stuff like this
           | in the past on Windows.
        
         | dec0dedab0de wrote:
         | Also not to be confused with watchdog and it's command
         | watchmedo, which is what I thought this was when I first read
         | the headline. I don't know what the differences are, but I use
         | watchdog to restart my local celery workers when I make
         | changes. It's scripted out so I don't ever use the command
         | directly.
         | 
         | https://pypi.org/project/watchdog/
         | 
         | https://github.com/gorakhargosh/watchdog
         | 
         | EDIT: Now I'm wondering how often I miss out on something
         | because I confuse it for something I already use. Maybe there
         | is room for a "things I use" or "things I know about" browser
         | plugin.
        
           | ghassanmas wrote:
           | Also to note "watchdog" the term. Is used also in
           | hardware/software context. Usually as a way to mitgate or
           | recover system faulate. I first I heard of this term while
           | taking in a course about PIC microcontroller[1].
           | 
           | > The Watchdog Timer in the PIC16F819 The watchdog timer can
           | be a real source of pain and can also make a PIC system
           | incredibly robust and reliable. But what exactly is the
           | watchdog timer? Simply put, the watchdog timer is a hardware
           | timer in the PIC that, if not constantly reset by the
           | software, will cause the PIC to reset. This feature can be
           | incredibly useful if the PIC should hang due to a hardware or
           | software issue and guarantees that the PIC will restart from
           | the beginning. Not only does it reset the system, it also
           | flags a bit that can be used to determine if the system just
           | crashed.
           | 
           | Ref: https://maker.pro/pic/tutorial/how-to-get-started-with-
           | pic-m...
           | 
           | [1]: PIC microcontroller, is a small hardware simliar to
           | arduino but more on a lower level.
        
           | damagednoob wrote:
           | Also not to be confused with watchexec (my tool of choice for
           | executing commands on file changes):
           | 
           | https://github.com/watchexec/watchexec
        
         | HelloNurse wrote:
         | 5. Windows is supported.
        
         | martincmartin wrote:
         | If you sign up for notifications on both github projects, you
         | can watch the watchmen.
        
           | graypegg wrote:
           | It's a good day when I can get a laugh out of HN heh
        
           | Manu40 wrote:
           | But then, who watches the watcher?
        
             | adhesive_wombat wrote:
             | Microsoft, via the analytics on GitHub.
        
               | MengerSponge wrote:
               | Ok, but then who watches the watchmen watcher?
        
               | [deleted]
        
               | Manu40 wrote:
               | Hardly comforting, but okay.
        
             | jinnko wrote:
             | Q
        
         | Semiapies wrote:
         | Alternately, Facebook's project is not to be confused with this
         | significantly older one.
        
           | amenghra wrote:
           | Both projects are from around 2014.
        
       | GRBurst wrote:
       | For those use-cases I use entr (https://github.com/clibs/entr).
       | In what sense does watchman differ to it? Didn't see anything
       | related to this at first glance :-)
        
         | DotaFan wrote:
         | OP did say he did this for learning purpose.
        
         | everybodyknows wrote:
         | The page linked above says:
         | 
         | > WARNING: This is a (possibly outdated and/or unmaintained)
         | fork of https://github.com/eradman/entr .
        
           | [deleted]
        
         | [deleted]
        
       | basemi wrote:
       | I've used `incron`[0] for that purpose, with success. Packaged
       | for most distros I think
       | 
       | [0]https://github.com/ar-/incron
        
       | thomasfl wrote:
       | Shameless plug for my own take on file watchers. The filewatcher
       | command line utility is available as a ruby gem. It exports shell
       | variables, so you can print name of files that changes this way:
       | $ filewatcher *.js 'echo $FILENAME'
       | 
       | It recently reached 1 million downloads. The current maintainer,
       | Alex Popov, is a very skilled developer working from Moscow.
        
       | Zamicol wrote:
       | Here was our take on the problem written in Go.
       | 
       | https://github.com/Cyphrme/watch
       | 
       | { "path_to_watch":"example_command_to_execute_on_change.sh", }
        
       ___________________________________________________________________
       (page generated 2022-10-05 23:00 UTC)