Lightweight and flexible way to execute a command when a file is updated

It’s often beneficial to trigger a specific command when a file is updated. For instance, when I write stuff in LaTeX I like to keep the resultant PDF open on one of my monitors to immediately see changes (and to see when I invariably mess up).

This means that I need to rebuild the PDF on every save.

A timed watch (e.g. every 30 seconds) works, as does a Gulp task using gulp-pdflatex[1] – though gulp is a bit heavyweight for such a simple task; it just feels wrong to pull in 8MB of node modules to run gulp watch. And then you have a single case – a task that only works for pdflatex, too. I like a more general solution.

So to react on a save without having the weight of gulp I have a custom script that looks something like the following:

#!/bin/zsh
inotifywait -e close_write,moved_to,create -m . |
while read -r directory events filename; do
  if [ "$filename" = "resume.tex" ]; then
    xelatex resume.tex
  fi
done

You’ll need inotify-tools (sudo apt install inotify-tools) if you don’t have it already.

You can check the detail of what is possible in the inotifywait man page. In short:

  • -e close_write,moved_to,create
    The events we watch for. Full list in the man page for inotifywait.
  • -m .
    monitor the current directory. Monitor also changes inotifywait from exiting after the first event (default behaviour) to run indefinitely.
  • read -r directory events filename;
    inotifywait outputs watched_filename EVENT_NAMES event_filename. In case of a watched directory a trailing slash is output for watched_filename
  • if [ "$filename" = "resume.tex" ]; then
    Make sure the file that changed is the file we are interested in
  • xelatex resume.tex
    Command we want to run on a change.

BTW, I originally had the inotifywait line something like:

inotifywait -q -m -e close_write --format %e myfile.ext

The downside being that inotifywait dies when the file is completely overwritten and many editors do exactly that.
So it’s easier to set up a watch on the whole directory that you are interested in and filter for specific files as done in the snippet above.

For interest, the --format %e in the old command is to handle spaces in the watched filenames – it replaces the output with a user specified format (in this case %e , return as comma-separated list of events).

Remember to chmod +x your script. Enjoy.

gulp-pdflatex

If you do not mind the size of all the node libs and want a special case that only works for pdflatex you can use a Gulpfile like:

var gulp = require('gulp');
var latex = require('gulp-latex');
var notify = require('gulp-notify');

gulp.task('pdflatex',function() {
	  return gulp.src('./*.tex')
		      .pipe( latex() )
          .pipe( gulp.dest('./out/') )
          .pipe(notify({
              title: "PDF Latex",
              message: "CV recreated",
              icon:'/home/user/Pics/Success.png'
          }));
});

gulp.task('watch', function() {
    gulp.watch(['./*.tex'], ['pdflatex']);
});

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s