File locks in bash
For quite a while I’ve been looking for a portable utility that mimics Procmail’s “lockfile” command. I didn’t need all the functionality, just for it to lock a single file and support a retry limit and sleep parameters. 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