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.