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