Preventing Duplicate Runs In A UNIX Shell Script

There are several ways to prevent a UNIX shell script from running multiple times simultaneously.  This post will describe a couple of ways to accomplish this.  Naturally, the first inclination generally  is to simply use ‘ps -ef’ to look for the presence of your script and exit if a currently executing copy is found.  This has several drawbacks, one of which is the tendency towards confusion when another  process is editing, tailing, or otherwise interacting with the script, yet it’s not really being executed, which is what we’re trying to prevent.  The two methods described here are daemons and PID files.

If there is a script that needs to be executed every minute, such as a monitoring tool, it can be scheduled in cron and the functionality to prevent duplicate runs can be embedded, which we’ll cover later.  Another alternative is to execute the script as a UNIX daemon.  This sounds a lot more complicated that it really is, so let’s dig into it.  First, the script will need to execute in a continuous loop, with some sort of sleep in between runs to prevent it from spinning on a CPU.  This is accomplished by updating the logic inside the script to something like this:

while true
do
  call_your_main_function

  sleep 30
done

Now, add it to the ‘/etc/inittab’ and the script will run continuously, without running over top of itself.  In Linux, edit the ‘/etc/inittab’ file and add a new entry:

newscr:35:respawn:/sbin/runuser -s /bin/ksh oracle -c /path_to_your_script/your_script_name

Where:

newscr    Abbreviated name for your script
35        Linux run levels that should be executing this script (3 & 5)
respawn   Automatically respawn the script if it dies
name      The name of the script to execute, with a fully qualified path.
          Also include something similar to the 'runser' command
          if the script is to be executed by non-root UNIX acccount.

Issue the ‘init q’ command to reload the ‘/etc/inittab’ and the script should start executing as a daemon.  AIX requires the use of the ‘mkitab’ command to create a new entry, but otherwise it works in a similar fashion.  Another advantage of this technique over cron is that the ‘sleep’ setting can be adjusted, thereby running a script on intervals less than one minute.  This would depend on the length of time required to execute the function inside the loop.

Another method for preventing duplicate runs is to use the very common Linux/UNIX concept of a PID file.  This is simply a file, in a known location, that contains the process ID of the executing script.  This file can be used as a lookup device to make sure the script matches the PID and exiting the script if needed.

A basic implementation of this requires very little coding.  At the top of the script, capture the command line that was used when the script was called:

# Save off the current script name with all command-line parameters
if [[ "$*" != "" ]]
then
  CURRENT_SCRIPT="$(print "$_" | awk -F \/ '{print $NF}') $*"
else
  CURRENT_SCRIPT="$(print "$_" | awk -F \/ '{print $NF}')"
fi

Then, name the ‘PID’ file somewhere in the top of the script:

PIDFILE="/var/tmp/myscript.pid"

Add a function that can check the ‘PID’ file against the system:

function already_running {
  # No PID file means it's not running
  if [[ ! -f $PIDFILE ]]
  then
    return 1
  fi

  if [[ $(ps -p $(cat $PIDFILE) -o args | grep -c "${CURRENT_SCRIPT}") -ne 0 ]]
  then
    return 0
  else
    return 1
  fi
}

Before any action occurs in the script, check and exit if it’s already being executed:

# Prevent multiple copies from running
if already_running
then
  # Print the results in an interactive shell
  if [[ -t 1 ]]
  then
    echo "Another copy is already running, exiting."
  fi

  exit 1
fi

Now,  save off the current PID if it wasn’t already running:

# Save off the current PID
echo "$$" > ${PIDFILE}

Hopefully, one of these two techniques will allow your script to implement duplicate-run prevention.

Advertisement
Tagged , , , ,

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Connecting to %s

Follow

Get every new post delivered to your Inbox.