I finally implemented one using Bash's "noclobber" option. I don't know if it will work correctly on NFS, but it should work fine on most filesystems. Hopefully it will be useful to some of you.
#!/bin/bash set -e declare SCRIPT_NAME="$(basename $0)" function usage { echo "Usage: $SCRIPT_NAME [options] <lock file>" echo "Options" echo " -r, --retries" echo " limit the number of retries before giving up the lock." echo " -s, --sleeptime, -<seconds>" echo " number of seconds between each attempt. Defaults to 8 seconds." exit 1 } #Check that at least one argument is provided if [ $# -lt 1 ]; then usage; fi declare RETRIES=-1 declare SLEEPTIME=8 #in seconds #Parse options for arg; do case "$arg" in -r|--retries) shift; if [ $# -lt 2 ]; then usage; fi; RETRIES="$1"; shift echo "$RETRIES" | egrep -q '^-?[0-9]+$' || usage #check that it's a number ;; -s|--sleeptime) shift; if [ $# -lt 2 ]; then usage; fi; SLEEPTIME="$1"; shift echo "$SLEEPTIME" | egrep -q '^[0-9]+$' || usage #check that it's a number ;; --) shift ; break ;; -[[:digit:]]*) if [ $# -lt 1 ]; then usage; fi; SLEEPTIME=${1:1}; shift echo "$SLEEPTIME" | egrep -q '^[0-9]+$' || usage #check that it's a number ;; --*) usage;; #fail on other options esac done #Check that only one argument is left if [ $# -ne 1 ]; then usage; fi declare lockfile="$1" for (( i=0; $RETRIES < 0 || i < $RETRIES; i++ )); do if ( set -o noclobber; echo "$$" > "$lockfile") 2> /dev/null; then exit 0 fi #Wait a bit sleep $SLEEPTIME done #Failed cat $lockfile exit 1