How Can I Tell If My Bash Script Is Already Running?

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