Jay Taylor's notes
back to listing indexlinux - How to make sure only one instance of a bash script runs? - Unix & Linux Stack Exchange
[web search]A solution that does not require additional tools would be prefered.
Almost like nsg's answer: use a lock directory. Directory creation is atomic under linux 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.
To add to Bruce Ediger's answer, and inspired by this answer, you should also add more smarts to the cleanup to guard against script termination:
#Remove the lock directory
function cleanup {
if rmdir $LOCKDIR; then
echo "Finished"
else
echo "Failed to remove lock directory '$LOCKDIR'"
exit 1
fi
}
if mkdir $LOCKDIR; then
#Ensure that if we "grabbed a lock", we release it
#Works for SIGTERM and SIGINT(Ctrl-C)
trap "cleanup" EXIT
echo "Acquired lock, running"
# Processing starts here
else
echo "Could not create lock directory '$LOCKDIR'"
exit 1
fi
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 -wv $$ | wc -l);
[[ $running > 1 ]] && exit;
# do stuff below this comment
grep -v $$
. real examples: old - 14532, new - 1453, old - 28858, new - 858.
– Naktibalda
Feb 22 '18 at 11:30
grep -wv "^$$"
(see edit).
– terdon♦
Feb 22 '18 at 12:38
One other way to make sure a single instance of bash script runs:
#!/bin/bash
# Check if another instance of script is running
pidof -o %PPID -x $0 >/dev/null && echo "ERROR: Script $0 already running" && exit 1
...
pidof -o %PPID -x $0
gets the PID of the existing script if its already running or exits with error code 1 if no other script is running
Although you've asked for a solution without additional tools, this is my favourite way using flock
:
#!/bin/sh
[ "${FLOCKER}" != "$0" ] && exec env FLOCKER="$0" flock -en "$0" "$0" "$@" || :
echo "servus!"
sleep 10
This comes from the examples section of man flock
, which further explains:
This is useful boilerplate code for shell scripts. Put it at the top of the shell script you want to lock and it'll automatically lock itself on the first run. If the env var $FLOCKER is not set to the shell script that is being run, then execute flock and grab an exclusive non-blocking lock (using the script itself as the lock file) before re-execing itself with the right arguments. It also sets the FLOCKER env var to the right value so it doesn't run again.
Points to consider:
- Requires
flock
, the example script terminates with an error if it can't be found - Needs no extra lock file
- May not work if the script is on NFS (see https://serverfault.com/questions/66919/file-locks-on-an-nfs)
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
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.
This is a modified version of Anselmo's Answer. The idea is to create a read only file descriptor using the bash script itself and use flock
to handle the lock.
SCRIPT=`realpath $0` # get absolute path to the script itself
exec 6< "$SCRIPT" # open bash script using file descriptor 6
flock -n 6 || { echo "ERROR: script is already running" && exit 1; } # lock file descriptor 6 OR show error message if script is already running
echo "Run your single instance code here"
The main difference to all other answer's is that this code doesn't modify the filesystem, uses a very low footprint and doesn't need any cleanup since the file descriptor is closed as soon as the script finishes independent of the exit state. Thus it doesn't matter if the script fails or succeeds.
I am using cksum to check my script is truly running single instance, even I change filename & file path.
I am not using trap & lock file, because if my server suddenly down, I need to remove manually lock file after server goes up.
Note: #!/bin/bash in first line is required for grep ps
#!/bin/bash
checkinstance(){
nprog=0
mysum=$(cksum $0|awk '{print $1}')
for i in `ps -ef |grep /bin/bash|awk '{print $2}'`;do
proc=$(ls -lha /proc/$i/exe 2> /dev/null|grep bash)
if [[ $? -eq 0 ]];then
cmd=$(strings /proc/$i/cmdline|grep -v bash)
if [[ $? -eq 0 ]];then
fsum=$(cksum /proc/$i/cwd/$cmd|awk '{print $1}')
if [[ $mysum -eq $fsum ]];then
nprog=$(($nprog+1))
fi
fi
fi
done
if [[ $nprog -gt 1 ]];then
echo $0 is already running.
exit
fi
}
checkinstance
#--- run your script bellow
echo pass
while true;do sleep 1000;done
Or you can hardcoded cksum inside your script, so you no worry again if you want to change filename, path, or content of your script.
#!/bin/bash
mysum=1174212411
checkinstance(){
nprog=0
for i in `ps -ef |grep /bin/bash|awk '{print $2}'`;do
proc=$(ls -lha /proc/$i/exe 2> /dev/null|grep bash)
if [[ $? -eq 0 ]];then
cmd=$(strings /proc/$i/cmdline|grep -v bash)
if [[ $? -eq 0 ]];then
fsum=$(grep mysum /proc/$i/cwd/$cmd|head -1|awk -F= '{print $2}')
if [[ $mysum -eq $fsum ]];then
nprog=$(($nprog+1))
fi
fi
fi
done
if [[ $nprog -gt 1 ]];then
echo $0 is already running.
exit
fi
}
checkinstance
#--- run your script bellow
echo pass
while true;do sleep 1000;done
Easiest one liner script rather than writing complex PID or lock statements
flock -xn LOCKFILE.lck -c SCRIPT.SH
where x denotes exclusive lock and n denotes non-blocking which fails rather than wait for the lock to be released. c runs the script that you want to launch.
My code to you
#!/bin/bash
script_file="$(/bin/readlink -f $0)"
lock_file=${script_file////_}
function executing {
echo "'${script_file}' already executing"
exit 1
}
(
flock -n 9 || executing
sleep 10
) 9> /var/lock/${lock_file}
Based on man flock
, improving only:
- the name of the lock file, to be based on the full name of the script
- the message
executing
Where I put here the sleep 10
, you can put all the main script.
This handy package does what you're looking for.
https://github.com/krezreb/singleton
once installed, just prefix your command with singleton LOCKNAME
e.g. singleton LOCKNAME PROGRAM ARGS...
You can use this: https://github.com/sayanarijit/pidlock
sudo pip install -U pidlock
pidlock -n sleepy_script -c 'sleep 10'
Linked
Related
Hot Network Questions
-
Stability of CI4 and PbI4
-
How can I deal with an underperforming peer that does not express interest in following team conventions?
-
What is the Russian word that sounds like "bleen"
-
Show that every contractible space is connected.
-
How can defeating lesser monster grants more magical power compared to those difficult ones?
-
At the start of Ursula Le Guin's "The Tombs of Atuan", who was the figure in black?
-
Is Relocating Necessary in Academia?
-
why do under-expanded engines have less than ideal thrust?
-
One app with more features vs. separate apps focused on one thing
-
Did Mark Zuckerberg create a website to rank his female classmates?
-
Why does the number of possible probability distributions have the cardinality of the continuum?
-
How critical is winding a solenoid/electromagnet neatly?
-
Where and how often do the exotic languages appear?
-
Zero the byte (eventually)
-
Any fundamental papers in TCS which were found to be incorrect/wrong later?
-
What is the real temperature to caramelize sugar?
-
Why don't I need a resistance when testing a light bulb circuit in a breadboard?
-
Is refusing offered stock options an option?
-
Is there a difference between playing off the beat and syncopation?
-
Why don’t astronauts “push” spacecraft?
-
What should an epidemic be called in Latin?
-
ifthen + [ngerman]babel + pgfplots + axis + text height = ☇
-
Is there a way to treat milk fever at home?
-
What languages are better fit for generating efficient code for 8-bit CPU's than C?
This site is not affiliated with Linus Torvalds or The Open Group in any way.