Often when writing scripts to run on a schedule, we want to avoid the same script running multiple instances at once, there is a frequently used pattern of handling this:

if [ -f /some/lock/file ]; then
    exit 1
fi

touch /some/lock/file

# our actual script...

rm -f /some/lock/file

Which just says if a lock file exists, stop doing what you're doing because the last script hasn't terminated yet. Simple and effective, but somewhat ugly.

Fortunately, the coreutils package contains a utility called flock which can handle locks for us.

So typically with the above script a crontab would just look like:

0 * * * * /myscript.sh

Instead we can remove that ugly lock file boilerplate from the script, and change our crontab to use flock:

0 * * * * flock /some/lock/file --command '/myscript.sh'

This functions a little bit differently however, because

By default, if the lock cannot be immediately acquired, flock waits until the lock is available.

We can actually change this to immediately fail if the lock can't be obtained using the --nonblock flag:

0 * * * * flock --nonblock /some/lock/file --command '/myscript.sh'

Benefits to using an flock approach:

  • Leaves the logic of locking out of your script
  • It works in the event of a script failing in between the touch, and the rm -f

Notes:

  • This actually does more than just create/delete a file, in fact it never deletes the lock file. It just checks if any process is currently making use of the lock file.
  • flock does much more than this, including trying to acquire a lock for a defined period of time before failing, properly handling child processes with the --close flag, and more.