Reputation: 188
When running a shell script I want to be absolutely sure that just a single instance of it runs at a given time.
Should I employ a file lock mechanism, like in
touch lockfile
do_work
rm lockfile
Is that enough?
Upvotes: 3
Views: 676
Reputation: 853
Either creates a lock file or fails (no race condition possible) while conveniently recording the shell's PID in the program's lock file. It Then cleans up if program is given any of the listed signals
#!/bin/bash
# Garbage clean up with trap given any of these SIGNALS
# INT Pressing Control-C kill -2 pid
# TERM Termination signal kill -15 pid
# EXIT Shell exit
LockFile="/tmp/${0}.lock"
if ( set -o noclobber; echo "$$" > "$LockFile") 2> /dev/null; then
trap 'rm -f "$LockFile"; exit $?' INT TERM EXIT
else
echo -e "Program \"$0\" already running"
exit 1
fi
Upvotes: 0
Reputation: 26066
Since flock
is not installed on some systems I use—and I jump between Ubuntu (which has it) and Mac OS X (which does not) a lot—I use this simple framework without any real issue:
LOCK_NAME="MY_GREAT_BASH_SCRIPT"
LOCK_DIR='/tmp/'${LOCK_NAME}.lock
PID_FILE=${LOCK_DIR}'/'${LOCK_NAME}'.pid'
if mkdir ${LOCK_DIR} 2>/dev/null; then
# If the ${LOCK_DIR} doesn't exist, then start working & store the ${PID_FILE}
echo $$ > ${PID_FILE}
echo "Hello world!"
rm -rf ${LOCK_DIR}
exit
else
if [ -f ${PID_FILE} ] && kill -0 $(cat ${PID_FILE}) 2>/dev/null; then
# Confirm that the process file exists & a process
# with that PID is truly running.
echo "Running [PID "$(cat ${PID_FILE})"]" >&2
exit
else
# If the process is not running, yet there is a PID file--like in the case
# of a crash or sudden reboot--then get rid of the ${LOCK_DIR}
rm -rf ${LOCK_DIR}
exit
fi
fi
The idea is that general core—where I have echo "Hello world!"
—is where the heart of your script is. The rest of it is basically a locking mechanism based on mkdir
. A good explanation of the concept is in this answer:
mkdir creates a directory if it doesn't exist yet, and if it does, it sets an exit code. More importantly, it does all this in a single atomic action making it perfect for this scenario.
Upvotes: 3
Reputation: 4317
It think that your logic is more like:
if [ ! -e lockfile ]; then
touch lockfile
do_work
rm lockfile
fi
But be aware that even this is not enough. Indeed, it paves the way for subtle bugs. Since the whole thing is not atomic, a second process might as well start just after the if
clause but before the touch
one.
A less naive implementation of your idea would attempt at atomicity of the lock-acquiring operation. This can be done in Bash.
A possible solution is the noclobber
option, which forbids writing to an already existing file, thereby accomplishing both the if
clause and the touch
command in a more-of-an-atomic fashion.
My lock-acquiring code would then look like this:
if ! ( set -o noclobber; echo > lockfile ) exit
Upvotes: 1
Reputation: 11479
You can use flock
for this :
(
flock -s 200
# .... commands executed under lock....
)200>/var/lock/mylockfile
It is available under the Util-linux next generation packaging... http://en.wikipedia.org/wiki/Util-linux
Upvotes: 3