Problem: You want your script to execute without overlapping another execution.
Solution: Make a flag file so you know that your script is already executing.
Extended problem: What if your script dies and leaves the flag file behind?
Extended solution: Check the process list to see if the old instance is still alive.
Ext. Ext. Problem: What if somebody kills my script?
Ext. Ext. Solution: Trap the abnormal exit and clean up properly.
Ext. Ext. Ext. Problem: What if someone else is invoking a script with exactly the same name as mine?
Ext. Ext. Ext. Solution: You’re screwed. Give your script a really long descriptive name so this won’t happen.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 | #=== FUNCTION ================================================================ # NAME: pidfilename # DESCRIPTION: create a predictable pid file name, put it in the right inode # PARAMETERS: none # RETURNS: path and filename #=============================================================================== function pidfilename() { myfile=$(basename "$0" .sh) whoiam=$(whoami) mypidfile=/tmp/$myfile.pid [[ "$whoiam" == 'root' ]] && mypidfile=/var/run/$myfile.pid echo $mypidfile } #=== FUNCTION ================================================================ # NAME: cleanup # DESCRIPTION: post service processing (clean temp space,pid files) # PARAMETERS: none # RETURNS: none #=============================================================================== function cleanup () { #Don't recurse in the exit trap trap - INT TERM EXIT #remove the pid file cleanly on exit [[ -f "$mypidfile" ]] && rm "$mypidfile" #add other post processing cleanup here exit } #=== FUNCTION ================================================================ # NAME: isrunning # DESCRIPTION: is any previous instance of this script already running # PARAMETERS: pidfile location # RETURNS: boolean 0|1 #=============================================================================== function isrunning() { pidfile="$1" [[ ! -f "$pidfile" ]] && return 1 #pid file is nonexistent procpid=$(<"$pidfile") [[ -z "$procpid" ]] && return 1 #pid file contains no pid # check process list for pid existence and is an instance of this script [[ ! $(ps -p $procpid | grep $(basename $0)) == "" ]] && value=0 || value=1 return $value } #=== FUNCTION ================================================================ # NAME: createpidfile # DESCRIPTION: atomic creation of pid file with no race condition # PARAMETERS: the pid to put in the file, the filename to use as a lock # RETURNS: none #=============================================================================== function createpidfile() { mypid=$1 pidfile=$2 #Close stderr, don't overwrite existing file, shove my pid in the lock file. $(exec 2>&-; set -o noclobber; echo "$mypid" > "$pidfile") [[ ! -f "$pidfile" ]] && exit #Lock file creation failed procpid=$(<"$pidfile") [[ $mypid -ne $procpid ]] && { #I'm not the pid in the lock file # Is the process pid in the lockfile still running? isrunning "$pidfile" || { # No. Kill the pidfile and relaunch ourselves properly. rm "$pidfile" $0 $@ & } exit } } mypidfile=$(pidfilename) createpidfile $$ "$mypidfile" # I win! set a trap for the lockfile on exit trap 'cleanup' INT TERM EXIT # Go ahead and do some processing sleep 10 |
I have been informed that there are a few corner cases that this script does not cover. For one, I may have write access to the pidfile, but not delete access. This would cause the second instance of the script to start a fork bomb. I can’t imagine a case where this would actually be true, so I’m not bothering to fix it. Second, this script forks itself in the background if the pid file is determined to be abandoned (i.e. power shut off). This might cause some calling script to return and proceed when the child is not finished. I’m not sure how to fix that without making the code extremely ugly, but when I think of something, I’ll stick it in here.
Scripting Help Please // Jul 25, 2011 at 5:40 am
[...] tags for your code. To create a file check the 'touch' command. This tutorial discusses pid files: http://kirk.webfinish.com/2009/10/ba…ationdeletion/ Have a look at this beginner's bash tutorial. It'll be handy [...]