A lot of times I have to write Bash scripts that get scheduled to run over and over again. These scripts can also take a while to run depending on the task I have for it. There could be a huge problem if I crank up multiple versions of the same script on accident and they are working on the same files and/or directories. So to solve this problem I’ll use a technique that creates lock files when the script is running and then removes it when it is finished running. Then I can just check for the existence of my lock file before I run the script and exit if it is already running.
To keep things nice and tidy I usually have a section to declare and define all of my variables. This is where I specify what the name of the lock file is going to be. I will generally base the lock file on the name of the currently executing script and just append a .lck file extension.
PDIR=${0%`basename $0`}
LCK_FILE=`basename $0`.lck
Now the very first thing I do in my file is check for the existence of my lock file. The -f option checks that the given file name is a regular file (not a directory or device). If it does not exist then I make the assumption that my script is not running and I create the lock file. Notice that I use the $$ system variable which returns the PID (process ID) of the currently running script. I simply echo this value into the lock file.
if [ -f "${LCK_FILE}" ]; then
...
else
echo "Not running"
echo $$ > "${LCK_FILE}"
fi
Well, if the file does exist then we need to make doubly sure that it is running. Why? Well the script may have terminated prematurely and not have had a chance to cleanup the lock file. So I grab the PID value out of the lock file and then use the ps command to see if a process with that PID is actually running. If it is not running then I again just echo $$ out to the lock file. If it is running I just exit.
MYPID=`head -n 1 "${LCK_FILE}"`
TEST_RUNNING=`ps -p ${MYPID} | grep ${MYPID}`
if [ -z "${TEST_RUNNING}" ]; then
# The process is not running
# Echo current PID into lock file
echo "Not running"
echo $$ > "${LCK_FILE}"
else
echo "`basename $0` is already running [${MYPID}]"
exit 0
fi
Well, that’s pretty much it! Now if you have to cron (schedule) a Bash script, you can rest assured that you won’t have multiple copies running at the same time. Here is the entire test script that I used. You can copy & paste it to use as a template for creating your own.
#!/bin/bash
# ------------------------------------------------------------
# File : AmIRunning
# Author : Jonathan Franzone
# Company : http://www.franzone.com
# Date : 09/23/2007
# Description : Test script for creating lock files
# ------------------------------------------------------------
# ------------------------------------------------------------
# Setup Environment
# ------------------------------------------------------------
PDIR=${0%`basename $0`}
LCK_FILE=`basename $0`.lck
# ------------------------------------------------------------
# Am I Running
# ------------------------------------------------------------
if [ -f "${LCK_FILE}" ]; then
# The file exists so read the PID
# to see if it is still running
MYPID=`head -n 1 "${LCK_FILE}"`
TEST_RUNNING=`ps -p ${MYPID} | grep ${MYPID}`
if [ -z "${TEST_RUNNING}" ]; then
# The process is not running
# Echo current PID into lock file
echo "Not running"
echo $$ > "${LCK_FILE}"
else
echo "`basename $0` is already running [${MYPID}]"
exit 0
fi
else
echo "Not running"
echo $$ > "${LCK_FILE}"
fi
# ------------------------------------------------------------
# Do Something
# ------------------------------------------------------------
while true
do
clear
echo
ls -F
echo
date
echo
sleep 5
done
# ------------------------------------------------------------
# Cleanup
# ------------------------------------------------------------
rm -f "${LCK_FILE}"
# ------------------------------------------------------------
# Done
# ------------------------------------------------------------
exit 0