It's very easy to implement locking in a bash script using mkdir
.
While there are other methods like:
- Touch a file and check its availability
- Use special tools like
lockfile-progs
in Debian orlockfile
in other distributions - Find the running process with
pgrep
I prefer mkdir
for simple bash scripts which only should run once at any given time. A folder can only be created once with the same name, if mkdir
is told to create this folder again, it ends with an error.
This error can be used to check if a script is already running. Of course there should be some cleanup if a script terminates before it cleanly ends or the script will never run again until you manually delete the folder. This can be done with traps, which executes cleanup functions on signals.
Here is an skeleton bash script which provides simple locking:
#!/bin/bash -
SCRIPTNAME=$(basename $0)
LOCK_DIR="/var/lock/${SCRIPTNAME}"
PIDFILE="${LOCK_DIR}/PID"
function lock {
mkdir $LOCK_DIR 2> /dev/null
[ $? == 0 ] && echo $$ > $PIDFILE
}
function remove_lock {
rm -rf $LOCK_DIR
}
function exit_on_error {
echo "Error, cleanup..."
remove_lock
UNLOCK=1
exit 1
}
function exit_on_kill {
echo "Kill, cleanup..."
remove_lock
UNLOCK=1
exit 1
}
function exit_on_end {
if [ "$UNLOCK" != "0" ]; then
echo "End, cleanup..."
remove_lock
UNLOCK=1
fi
exit $1
}
function lock_failed {
# check for PID
PID=$(cat $PIDFILE)
# Maybe there was an error reading the PID file
if [ $? != 0 ]; then
echo "Locking failed, PID ${PID} is active" >&2
UNLOCK=1
exit 1
fi
# Check if the PID is really existing
if ! kill -0 $PID &>/dev/null; then
echo "Removing stale lock of nonexistant PID ${PID}" >&2
remove_lock
echo "Restarting myself (${SCRIPTNAME})" >&2
exec "$0" "$@"
else
echo "Locking failed, PID ${PID} is active" >&2
UNLOCK=1
exit 1
fi
UNLOCK=1
exit 1
}
trap exit_on_kill KILL
trap exit_on_error ERR
trap 'exit_on_end $?' QUIT TERM EXIT INT
lock || lock_failed
### My script:
echo "Hello World"
sleep 10
This skeleton is inspired by the following articles: Lock your script (against parallel run) and Manage locking into bash scripts. Thanks!
Some things needs explanations:
- At the end of every bash script, the Trap handler 0 is called. Using the variable
UNLOCK
I can check ifexit_on_end
is called the second time. Otherwise everytime the script ends the lock is removed, regardless if it should or not. exit_on_end
is called with the current exit code when it's called to preserve it- If the cleanup did not succeed, the check if the PID really exists can handle this case
Start your script below ### My script:
and have some fun.