ProgrammingThis forum is for all programming questions.
The question does not have to be directly related to Linux and any language is fair game.
Notices
Welcome to LinuxQuestions.org, a friendly and active Linux Community.
You are currently viewing LQ as a guest. By joining our community you will have the ability to post topics, receive our newsletter, use the advanced search, subscribe to threads and access many other special features. Registration is quick, simple and absolutely free. Join our community today!
Note that registered members see fewer ads, and ContentLink is completely disabled once you log in.
If you have any problems with the registration process or your account login, please contact us. If you need to reset your password, click here.
Having a problem logging in? Please visit this page to clear all LQ-related cookies.
Get a virtual cloud desktop with the Linux distro that you want in less than five minutes with Shells! With over 10 pre-installed distros to choose from, the worry-free installation life is here! Whether you are a digital nomad or just looking for flexibility, Shells can put your Linux machine on the device that you want to use.
Exclusive for LQ members, get up to 45% off per month. Click here for more info.
I have a bash script which is called automatically
I want it so that if when the script is called if a previous instance of it is already running then to delay the running of it until the previous instance has stopped (effectively queue up ./script.sh var1 var2)
I have seen some posts about a 'lockfile' but this just seems to stop the second instance running rather than queueing it up to run next (it also needs to be able to queue up a 3rd/4th calling of the script and run them one at a time)
any suggestions?
p.s. the script is run using cygwin on windows
edit: I am very new to bash scripting so simple explanations please
You can try this code. I think the documentation's enough but you can ask me if there's something you don't understand. I hope you'll also refer to the bash manual first (man bash).
Code:
#!/bin/bash
INSTANCEDIR=/path/somewhere
function error {
# send message to stderr
echo "$1" >&- >&2
}
function main {
local MYPID=$$ FILE PID
local -a INSTANCEFILES
echo "$MYPID" >> "$INSTANCEDIR/$MYPID" || {
error "failed to record instance to $INSTANCEDIR/$MYPID"
return 1
}
for (( ;; )); do
INSTANCEFILES=("$INSTANCEDIR"/*)
case "${#INSTANCEFILES[@]}" in
1)
break
;;
0)
error "our instance file has been deleted."
return 1
;;
esac
for FILE in "${INSTANCEFILES[@]}"; do
# read PID from instance file
PID=$(<"$FILE")
# delete the instance file if read data in it is not a digitt
if [[ $PID != +([[:digit:]]) ]]; then
rm "$FILE" || {
error "failed to delete unowned instance file $FILE."
return 1
}
fi
# skip the file if it's from us
[[ PID -eq MYPID ]] && continue
# Delete the instance file if the process presented by the PID no longer exists
# There might also be a better way to check the process but I'm not sure.
if ! kill -s 0 "$PID"; then
rm "$FILE" || {
error "failed to delete unowned instance file $FILE."
return 1
}
fi
done
done
# continue the operations from here
}
---- EDIT ----
Other than this there should also be other methods like locking files with exec and using fuser to check if a process is still locked with the file; and also repeatedly using rm -f to locked files until the files count turn 1..... Although I still can't see cleaner implementation in those ways and I also find some of it as quirky.
You may also just use a single instance file to reference but I didn't choose it since I see a very tiny possibility that a race condition could occur. I get a feeling that other optimistic (ones who'll tend to say "that's ok") programmers may argue with this.
You might also want to add a code that automatically checks and/or creates INSTANCEDIR.
Last edited by konsolebox; 09-12-2010 at 07:18 AM.
@konsolebox
not sure if I executed your script as it was intended to. This is what I did:
Code:
#!/bin/bash
# script.sh
error {
...
}
main {
...
}
main
# just do something unspectacular
for i in $(seq 1 10);do
echo loop $i of script $1
sleep 1
done
exit
Then I created another script to call it several times:
$ ./caller.sh
loop 1 of script 1
loop 2 of script 1
loop 3 of script 1
$ loop 4 of script 1
loop 5 of script 1
loop 6 of script 1
loop 7 of script 1
loop 8 of script 1
loop 9 of script 1
loop 10 of script 1
./script.sh: line 50: kill: (1041) - No such process
./script.sh: line 50: kill: (1041) - No such process
rm: cannot remove `./lock/1041': No such file or directory
failed to delete unowned instance file ./lock/1041.
loop 1 of script 3
loop 2 of script 3
loop 3 of script 3
loop 4 of script 3
loop 5 of script 3
loop 6 of script 3
loop 7 of script 3
loop 8 of script 3
loop 9 of script 3
loop 10 of script 3
./script.sh: line 50: kill: (2296) - No such process
./script.sh: line 35: ./lock/2296: No such file or directory
rm: cannot remove `./lock/2296': No such file or directory
failed to delete unowned instance file ./lock/2296.
loop 1 of script 2
loop 2 of script 2
loop 3 of script 2
loop 4 of script 2
loop 5 of script 2
loop 6 of script 2
loop 7 of script 2
loop 8 of script 2
loop 9 of script 2
loop 10 of script 2
./script.sh: line 50: kill: (1047) - No such process
loop 1 of script 4
loop 2 of script 4
loop 3 of script 4
loop 4 of script 4
loop 5 of script 4
loop 6 of script 4
loop 7 of script 4
loop 8 of script 4
loop 9 of script 4
loop 10 of script 4
$ ./caller.sh
loop 1 of script 1
loop 2 of script 1
loop 3 of script 1
$ loop 4 of script 1
loop 5 of script 1
loop 6 of script 1
loop 7 of script 1
loop 8 of script 1
loop 9 of script 1
loop 10 of script 1
./script.sh: line 50: kill: (3258) - No such process
./script.sh: line 50: kill: (3258) - No such process
./script.sh: line 50: kill: (3258) - No such process
rm: cannot remove `./lock/3258': No such file or directory
failed to delete unowned instance file ./lock/3258.
rm: cannot remove `./lock/3258': No such file or directory
failed to delete unowned instance file ./lock/3258.
loop 1 of script 3
loop 1 of script 2
loop 2 of script 3
loop 2 of script 2
loop 3 of script 3
loop 3 of script 2
loop 4 of script 3
loop 4 of script 2
loop 5 of script 3
loop 5 of script 2
loop 6 of script 3
loop 6 of script 2
loop 7 of script 3
loop 7 of script 2
loop 8 of script 3
loop 8 of script 2
loop 9 of script 3
loop 9 of script 2
loop 10 of script 3
loop 10 of script 2
./script.sh: line 50: kill: (4380) - No such process
./script.sh: line 50: kill: (3268) - No such process
loop 1 of script 4
loop 2 of script 4
loop 3 of script 4
loop 4 of script 4
loop 5 of script 4
loop 6 of script 4
loop 7 of script 4
loop 8 of script 4
loop 9 of script 4
loop 10 of script 4
The first time the scripts were executed in the wrong order. The second time apparently the lock did not work 100% and the ouputs of script 2 and 3 interleaved.
But as I said, I am not sure if I used it right.
<removed: fawlty script>
Last edited by crts; 09-12-2010 at 10:00 AM.
Reason: removed script
edit: mistakenly pressed quote button for edit.. guess i'll better just post a new complete code. The error messages of kill -s 0 "$PID" is now sent to null.
Code:
#!/bin/bash
shopt -s extglob
INSTANCEDIR=/path/somewhere
function error {
# send message to stderr
echo "$1" >&- >&2
}
function main {
local MYPID=$$ FILE PID
local -a INSTANCEFILES
echo "$MYPID" >> "$INSTANCEDIR/$MYPID" || {
error "failed to record instance to $INSTANCEDIR/$MYPID"
return 1
}
for (( ;; )); do
INSTANCEFILES=("$INSTANCEDIR"/*)
case "${#INSTANCEFILES[@]}" in
1)
break
;;
0)
error "our instance file has been deleted."
return 1
;;
esac
for FILE in "${INSTANCEFILES[@]}"; do
# read PID from instance file
PID=$(<"$FILE")
# delete the instance file if read data in it is not a digitt
if [[ $PID != +([[:digit:]]) ]]; then
rm "$FILE" || {
error "failed to delete unowned instance file $FILE."
return 1
}
fi
# skip the file if it's from us
[[ PID -eq MYPID ]] && continue
# Delete the instance file if the process presented by the PID no longer exists
# There might also be a better way to check the process but I'm not sure.
if ! kill -s 0 "$PID" 2>/dev/null; then
rm "$FILE" || {
error "failed to delete unowned instance file $FILE."
return 1
}
fi
done
read -t 1 -n 1
done
# continue the operations from here
}
main
update: added 'read -t 1 -n 1'
Last edited by konsolebox; 09-12-2010 at 11:20 AM.
I decided to put /path/to/script.sh $1 $2 and call singleinstance.sh $1 $2 in place of the original call (I find it easier to keep the 2 pieces of code separate) but script.sh is saying var 1 is null so it doesn't seem to be passing them
the script has been running good for a number of days
but lately not sure why sometimes the script hangs not sure if it is the single instance one or the script i am running through it (need to look into it more - have been away for a few days)
and then any other instances get queued up and don't run as the first one never exits
could there be an issue if it is called twice very quickly i.e. within 1 second or less?
also could it be made so that if the instance has been running for 5 minutes it terminates (therefore allowing any other queued instances to start)?
This is due to it running out of PIDs. I noted the problem and solution on Swhack; basically you have to run "rebaseall", but that means closing down all of your current processes and actually running it in bash which is somewhat irritating. If anyone has a better solution, I'd be glad if they could let me know. Thanks.
also stackdump:
Quote:
Exception: STATUS_ACCESS_VIOLATION at eip=61020137
eax=00E0D008 ebx=6123A614 ecx=764C0D06 edx=003441B8 esi=00000000 edi=0028F9E8
ebp=61020890 esp=0028C7B8 program=C:\cygwin\bin\sh.exe, pid 6656, thread main
cs=0023 ds=002B es=002B fs=0053 gs=002B ss=002B
Stack trace:
Frame Function Args
End of stack trace
LinuxQuestions.org is looking for people interested in writing
Editorials, Articles, Reviews, and more. If you'd like to contribute
content, let us know.