Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to make sure only one instance of a Bash script is running at a time?

I want to make a sh script that will only run at most once at any point.

Say, if I exec the script then I go to exec the script again, how do I make it so that if the first exec of the script is still working the second one will fail with an error. I.e. I need to check if the script is running elsewhere before doing anything. How would I go about doing this??

The script I have runs a long running process (i.e. runs forever). I wanted to use something like cron to call the script every 15mins so in case the process fails, it will be restarted by the next cron run script.

like image 559
NightWolf Avatar asked Jun 26 '11 07:06

NightWolf


People also ask

What is $@ in bash?

bash [filename] runs the commands saved in a file. $@ refers to all of a shell script's command-line arguments. $1 , $2 , etc., refer to the first command-line argument, the second command-line argument, etc. Place variables in quotes if the values might have spaces in them.

How do you lock a file in shell script?

The "|| exit 1" instructs the script to exit if the exec command fails. The above line obtains a lock on file descriptor 100. Basically holding a lock on the file until the shell closes. Since scripts run in a sub-shell, the file will be closed (or unlocked) when the script exits.

What is flock in shell script?

It locks a specified file, which is created (assuming appropriate permissions), if it does not already exist. The second form is conveninent inside shell scripts, and is usually used the following manner: ( flock -s 200.


5 Answers

You want a pid file, maybe something like this:

pidfile=/path/to/pidfile
if [ -f "$pidfile" ] && kill -0 `cat $pidfile` 2>/dev/null; then
    echo still running
    exit 1
fi  
echo $$ > $pidfile
like image 109
cxreg Avatar answered Nov 14 '22 23:11

cxreg


I think you need to use lockfile command. See using lockfiles in shell scripts (BASH) or http://www.davidpashley.com/articles/writing-robust-shell-scripts.html.

The second article uses "hand-made lock file" and shows how to catch script termination & releasing the lock; although using lockfile -l <timeout seconds> will probably be a good enough alternative for most cases.

Example of usage without timeout:

lockfile script.lock
<do some stuff>
rm -f script.lock

Will ensure that any second script started during this one will wait indefinitely for the file to be removed before proceeding.

If we know that the script should not run more than X seconds, and the script.lock is still there, that probably means previous instance of the script was killed before it removed script.lock. In that case we can tell lockfile to force re-create the lock after a timeout (X = 10 below):

lockfile -l 10 /tmp/mylockfile
<do some stuff>
rm -f /tmp/mylockfile

Since lockfile can create multiple lock files, there is a parameter to guide it how long it should wait before retrying to acquire the next file it needs (-<sleep before retry, seconds> and -r <number of retries>). There is also a parameter -s <suspend seconds> for wait time when the lock has been removed by force (which kind of complements the timeout used to wait before force-breaking the lock).

like image 20
alexandroid Avatar answered Nov 14 '22 22:11

alexandroid


You can use the run-one package, which provides run-one, run-this-one and keep-one-running.

The package: https://launchpad.net/ubuntu/+source/run-one

The blog introducing it: http://blog.dustinkirkland.com/2011/02/introducing-run-one-and-run-this-one.html

like image 30
blueyed Avatar answered Nov 14 '22 23:11

blueyed


Write the process id into a file and then when a new instance starts, check the file to see if the old instance is still running.

like image 27
Noufal Ibrahim Avatar answered Nov 14 '22 21:11

Noufal Ibrahim


(
        if flock -n 9
        then
                echo 'Not doing the critical operation (lock present).'
                exit;
        fi

        # critical section goes here

) 9>'/run/lock/some_lock_file'
rm -f '/run/lock/some_lock_file'

From example in flock(1) man page. Very practical for using in shell scripts.

like image 42
Josef Kufner Avatar answered Nov 14 '22 21:11

Josef Kufner