views:

977

answers:

3

At a previous workplace we used to have a highly-refined bash function called run-only-once that we could write into any long-running shell script, and then call it at the start of the script, which would check to see whether the script was already running as another process, and if so exit with a notification to STDOUT.

Does anyone have a function/fragment like this they could share?

Our old function (which I no longer have) would check for a PID file (in scriptname.$$ format) in /var/run, and use then either exit or simply continue. In the case where a PID file existed, it would do some checks to make sure that the process was still active. It also had a few options for controlling whether a notification was output at all.

From memory, our function only worked in bash. Bonus points for a /bin/sh version.

+1  A: 

Put this at the start of the script

SCRIPTNAME=`basename $0`
PIDFILE=/var/run/${SCRIPTNAME}.pid


if [ -f ${PIDFILE} ]; then
   #verify if the process is actually still running under this pid
   OLDPID=`cat ${PIDFILE}`
   RESULT=`ps -ef | grep ${OLDPID} | grep ${SCRIPTNAME}`  

   if [ -n "${RESULT}" ]; then
     echo "Script already running! Exiting"
     exit 255
   fi

fi


#grab pid of this process and update the pid file with it
PID=`ps -ef | grep ${SCRIPTNAME} | head -n1 |  awk ' {print $2;} '`
echo ${PID} > ${PIDFILE}

and at the end

if [ -f ${PIDFILE} ]; then
    rm ${PIDFILE}
fi

This first of all checks for the existence of the pid file and exits if it's present. If so then it confirms that a process under this script name with the old pid is running and exits if so. If not then it carries on and updates the script with the new pid file. The bit at the end checks for the existence of the pid file and deletes it, so the script can run next time.

Check permissions on /var/run are OK for your script though, otherwise create the PID file in another directory. Same directory as the script runs in would be fine.

NeilInglis
Good answer, I'll probably use it, or a combination of this and the answer on the question 185451 that Nik points to (i.e. I'll use trap and kill -0).
Anon Guy
Also, can you not use $$ to get the PID of this process?
Anon Guy
+1  A: 

This looks pretty much like a duplicate of 185451.

185451: "Quick-and-dirty way to ensure only one instance of a shell script is running at a time"

nik
Yup you're right. Plus that one contains a better solution that mine!
NeilInglis
Nope, that one just has bad answers.
R..
+1  A: 

There are two ways to do atomic locks from the shell. The simplest and most portable is mkdir. Creation of a directory will always fail if a file/directory by that name already exists, so simply use a "lock directory" instead of "lock file".

mkdir "$LOCK" || { echo "Script already running" ; exit 1 ; }

The other method is the noclobber option, set using set -C. This option forces the shell to open files for output redirection with the O_EXCL flag. Use it like this:

set -C
> "$LOCK" || { echo "Script already running" ; exit 1 ; }
set +C

Rather than redirecting a null command, you might prefer to echo the current PID or something else useful to be stored in the file.

It's worth keeping in mind that some ancient historic shells have broken implementations of the noclobber option -C which do not use O_EXCL but instead buggy non-atomic checks for file existence. Probably not an issue in the 21st century, but you should be aware just in case.

R..