Reputation: 4376
I am a bit confused about process control within a bash script.
What I want, is to run specific functions/routines and at 10:00 stop them and then run some other functions. When the "other functions" end, I want the first functions continue exactly from the spot they were when they were stopped.
For example, suppose I run main.sh at 09:50. I want the function_one to stop when time is 10:00, run function_two and then continue with function_one at the exact state it was. Needless to say, that function_one could be quite complex, and have background child processes itself.
$ cat main.sh
#!/bin/bash
source functions.sh
function_one &
echo $! > function_one_running_in_background_PID.txt
while true; do
sleep 10s
if [[ $(date +%H%M) = 1000 ]]; then
kill -SIGSTOP $(<function_one_running_in_background_PID.txt)
echo Time is 10
function_two
kill -SIGCONT $(<function_one_running_in_background_PID.txt)
fi
done
$ cat functions.sh
#!/bin/bash
function function_one {
for i in {1..100000}; do echo 1st function: $i; sleep 1; done
}
function function_two {
for i in {1..100}; do echo 2nd function: $i; sleep 1; done
}
Could you please tell me if the above is possible? "Maybe" I am missing something in bash. Maybe there is a better way to do it instead of what I have thought.
Thank you all!
Upvotes: 2
Views: 983
Reputation: 4376
Thank you all for your immediate answers and I apologize for the delay. I had a system disk drive failure (!!)
Janos, what happens if f1 has background processes in it? How could I stop all of them?
#!/bin/sh -e
f1() {
f3 &
f4 &
}
f2() { for i in {1..1000}; do echo f2 $i; sleep 1; done; }
f3() { for i in {1..1000}; do echo f3 $i; sleep 1; done; }
f4() { for i in {1..1000}; do echo f4 $i; sleep 1; done; }
f1 &
PID1=$!
f2 &
PID2=$!
kill -STOP $PID2
while :; do
if [[ $(date +%S) = *0 ]]; then
kill -STOP $PID1
kill -CONT $PID2
elif [[ $(date +%S) = *5 ]]; then
kill -STOP $PID2
kill -CONT $PID1
fi
sleep 1
done
Upvotes: 0
Reputation: 124704
I think this can work. Some notes:
Even if you stop a process, its child processes will continue to run happily. If you want to stop those as well, then you need to trap STOP and CONT signals in the parent to stop and resume the children.
Instead saving the PIDs in files, why not save in variables?
You can shorten kill -SIGSTOP
as kill -STOP
, same for CONT
Here's a sample demo code to play with:
#!/bin/sh -e
f1() { for i in {1..1000}; do echo f1 $i; sleep 1; done; }
f2() { for i in {1..1000}; do echo f2 $i; sleep 1; done; }
f1 &
PID1=$!
f2 &
PID2=$!
kill -STOP $PID2
turn=1
while :; do
if test $turn = 1; then
kill -STOP $PID1
kill -CONT $PID2
turn=2
elif test $turn = 2; then
kill -STOP $PID2
kill -CONT $PID1
turn=1
fi
sleep 5
done
Upvotes: 1
Reputation: 2332
To catch all the children I suppose they will belong to the same process-group, initiated by the program that launches them. Killing the group will kill all children, grand children etc.
It could be as simple as this:
#!/bin/bash
#set the 'at' timer on 22:00 to give self a signal (see 'man at' )
at 22:00 <<<"/usr/bin/kill -SIGCONT $$"
#gentlemen...start your engines..ehrm..programs!
"/path/firstset/member1.sh" &
"/path/firstset/member2.sh" &
"/path/firstset/member3.sh" &
"/path/firstset/member4.sh" &
while :
do
kill -SIGSTOP "$$" #<--now we halt this script.
# wait for SIGCONT given by 'at'
# Very nicely scheduled on 22:00
#when waking up:
PLIST=$(pgrep -g "$$") # list PIDs of group (= self + subtree)
PLIST=${PLIST/"$$"/} # but not self, please
kill -SIGSTOP $PLIST #OK it is 22:00, halt the old bunch
"/path/secondset/member1.sh" & # start the new bunch
"/path/secondset/member2.sh" &
"/path/secondset/member3.sh" &
"/path/secondset/member4.sh" &
wait # wait till they are finished
kill -SIGCONT $PLIST #old bunch can continue now,
done #until next scheduled event.
Upvotes: 1
Reputation: 531693
Rather than run a busy loop that constantly checks if it is 10:00, just figure out how many seconds until 10:00 and sleep that long before stopping the first process. There's no need for a temporary file, as you can just store the process ID in another parameter until needed.
t=$(( $(date +%s --date 1000) - $(date +%s) ))
function_one & f1_pid=$!
sleep $t
kill -STOP $f1_pid
function_two
kill -CONT $f1_pid
If function_one
forks a child of its own, you will have to make sure that it is written in such a way to stop them when it is stopped itself.
Upvotes: 1