What is the best way to ensure only one instance of a Bash script is running? [duplicate]
Advisory locking has been used for ages and it can be used in bash scripts. I prefer simple flock
(from util-linux[-ng]
) over lockfile
(from procmail
). And always remember about a trap on exit (sigspec == EXIT
or 0
, trapping specific signals is superfluous) in those scripts.
In 2009 I released my lockable script boilerplate (originally available at my wiki page, nowadays available as gist). Transforming that into one-instance-per-user is trivial. Using it you can also easily write scripts for other scenarios requiring some locking or synchronization.
Here is the mentioned boilerplate for your convenience.
#!/bin/bash# SPDX-License-Identifier: MIT## Copyright (C) 2009 Przemyslaw Pawelczyk <przemoc@gmail.com>#### This script is licensed under the terms of the MIT license.## https://opensource.org/licenses/MIT## Lockable script boilerplate### HEADER ###LOCKFILE="/var/lock/`basename $0`"LOCKFD=99# PRIVATE_lock() { flock -$1 $LOCKFD; }_no_more_locking() { _lock u; _lock xn && rm -f $LOCKFILE; }_prepare_locking() { eval "exec $LOCKFD>\"$LOCKFILE\""; trap _no_more_locking EXIT; }# ON START_prepare_locking# PUBLICexlock_now() { _lock xn; } # obtain an exclusive lock immediately or failexlock() { _lock x; } # obtain an exclusive lockshlock() { _lock s; } # obtain a shared lockunlock() { _lock u; } # drop a lock### BEGIN OF SCRIPT #### Simplest example is avoiding running multiple instances of script.exlock_now || exit 1# Remember! Lock file is removed when one of the scripts exits and it is# the only script holding the lock or lock is not acquired at all.
If the script is the same across all users, you can use a lockfile
approach. If you acquire the lock, proceed else show a message and exit.
As an example:
[Terminal #1] $ lockfile -r 0 /tmp/the.lock[Terminal #1] $ [Terminal #2] $ lockfile -r 0 /tmp/the.lock[Terminal #2] lockfile: Sorry, giving up on "/tmp/the.lock"[Terminal #1] $ rm -f /tmp/the.lock[Terminal #1] $ [Terminal #2] $ lockfile -r 0 /tmp/the.lock[Terminal #2] $
After /tmp/the.lock
has been acquired your script will be the only one with access to execution. When you are done, just remove the lock. In script form this might look like:
#!/bin/bashlockfile -r 0 /tmp/the.lock || exit 1# Do stuff hererm -f /tmp/the.lock
I think flock
is probably the easiest (and most memorable) variant. I use it in a cron job to auto-encode dvds and cds
# try to run a command, but fail immediately if it's already runningflock -n /var/lock/myjob.lock my_bash_command
Use -w
for timeouts or leave out options to wait until the lock is released. Finally, the man page shows a nice example for multiple commands:
( flock -n 9 || exit 1 # ... commands executed under lock ... ) 9>/var/lock/mylockfile