Take the 2-minute tour ×
Unix & Linux Stack Exchange is a question and answer site for users of Linux, FreeBSD and other Un*x-like operating systems.. It's 100% free, no registration required.

A solution that does not require additional tools would be prefered.

share|improve this question
    
What about a lock file? –  Marco Sep 18 '12 at 11:07
    
@Marco I found this SO answer using that, but as stated in a comment, this can create a race condition –  Tobias Kienzler Sep 18 '12 at 11:18
2  
This is BashFAQ 45. –  jw013 Sep 18 '12 at 13:49
    
@jw013 thanks! So maybe something like ln -s my.pid .lock will claim the lock (followed by echo $$ > my.pid) and on failure can check whether the PID stored in .lock is really an active instance of the script –  Tobias Kienzler Sep 18 '12 at 15:22

4 Answers 4

up vote 5 down vote accepted

Almost like nsg's answer: use a lock directory. Directory creation is atomic under linu and unix and *BSD and a lot of other OSes.

if mkdir $LOCKDIR
then
    # Do important, exclusive stuff
    if rmdir $LOCKDIR
    then
        echo "Victory is mine"
    else
        echo "Could not remove lock dir" >&2
    fi
else
    # Handle error condition
    ...
fi

You can put the PID of the locking sh into a file in the lock directory for debugging purposes, but don't fall into the trap of thinking you can check that PID to see if the locking process still executes. Lots of race conditions lie down that path.

share|improve this answer
1  
I'd consider using the stored PID to check whether the locking instance is still alive. However, here's a claim that mkdir is not atomic on NFS (which is not the case for me, but I guess one should mention that, if true) –  Tobias Kienzler Sep 18 '12 at 13:08
    
Yes, by all means use the stored PID to see if the locking process still executes, but don't attempt to do anything other than log a message. The work of checking the stored pid, creating a new PID file, etc, leaves a big window for races. –  Bruce Ediger Sep 18 '12 at 13:37
    
Ok, as Ihunath stated, the lockdir would most likely be in /tmp which is usually not NFS shared, so that should be fine. –  Tobias Kienzler Sep 19 '12 at 8:33
    
I would use rm -rf to remove the lock directory. rmdir will fail if someone (not necessarily you) managed to add a file to the directory. –  chepner Sep 22 '12 at 4:32

I would use a lock file, as mentioned by Marco

#!/bin/bash

# Exit if /tmp/lock.file exists
[ -f /tmp/lock.file ] && exit

# Create lock file, sleep 1 sec and verify lock
echo $$ > /tmp/lock.file
sleep 1
[ "x$(cat /tmp/lock.file)" == "x"$$ ] || exit

# Do stuff
sleep 60

# Remove lock file
rm /tmp/lock.file
share|improve this answer
1  
(I think you forgot to create the lock file) What about race conditions? –  Tobias Kienzler Sep 18 '12 at 11:28
    
ops :) Yes, race conditions is a problem in my example, I usually write hourly or daily cron jobs and race conditions are rare. –  nsg Sep 18 '12 at 11:32
    
They shouldn't be relevant in my case either, but it's something one should keep in mind. Maybe using lsof $0 isn't bad, either? –  Tobias Kienzler Sep 18 '12 at 11:34
    
You can diminish the race condition by writing your $$ in the lock file. Then sleep for a short interval and read it back. If the PID is still yours, you successfully acquired the lock. Needs absolutely no additional tools. –  manatwork Sep 18 '12 at 11:41
1  
I have never used lsof for this purpose, I this it should work. Note that lsof is really slow in my system (1-2 sec) and most likely there is a lot of time for race conditions. –  nsg Sep 18 '12 at 11:45

If you want to make sure that only one instance of your script is running take a look at:

Lock your script (against parallel run)

Otherwise you can check ps or invoke lsof <full-path-of-your-script>, since i wouldn't call them additional tools.


Supplement:

actually i thought of doing it like this:

for LINE in `lsof -c <your_script> -F p`; do 
    if [ $$ -gt ${LINE#?} ] ; then
        echo "'$0' is already running" 1>&2
        exit 1;
    fi
done

this ensures that only the process with the lowest pid keeps on running even if you fork-and-exec several instances of <your_script> simultaneously.

share|improve this answer
1  
Thanks for the link, but could you include the essential parts in your answer? It's common policy at SE to prevent link rot... But something like [[(lsof $0 | wc -l) > 2]] && exit might actually be enough, or is this also prone to race conditions? –  Tobias Kienzler Sep 18 '12 at 11:30
    
You are right the essential part of my answer was missing and only posting links is pretty lame. I added my own suggestion to the answer. –  user1146332 Sep 18 '12 at 12:52

This may be too simplistic, please correct me if I'm wrong. Isn't a simple ps enough?

#!/bin/bash 

me=`basename $0`;
running=`ps h -C $me | grep -v $$ | wc -l`;
[[ $running > 1 ]] && exit;

do stuff...
share|improve this answer

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.