bash - killing a subprocess after a set timeout - bash

I was hoping somebody would be able to help me with this
I need a loop for a shell script that will run what is inside the loop for 15 seconds. SO for example
if (true)
run command for 15 seconds
fi
kill PID
I am new to shell scripting, so i am lost with this.
Also I am using a debian instll if that makes any difference
Any help is appreciated

Are you looking for the timeout command?

The following bash script might work for you. The script will set the initial epoch time as a variable prior to beginning a loop. While the loop runs an additional variable will be set with the current epoch time. Both epoch times will be compared and as long as the difference is less than or equal to 15 your command will continue to run. Note that in the script below the current command running is 'echo "counting ${COUNTER}"'. You should change this portion of the script to match what you are trying to accomplish. Once the difference of the two epoch times is greater than 15 the script will exit. You will need to initate your kill command at this point. If an error does occur you should see "ERROR... YourScript.sh failed" in "YourLogFile" (set your log file to what you would like)
NOTE: Whatever you are attempting to run while inside this loop may run many many many times within the 15 second period. By utilizing the script below as a test you will see that the echo command runs more than 50 times per second.
#!/bin/bash
LOOP="true"
INITIAL_TIME=$(date "+%s")
while [[ ${LOOP} == true ]]; do
CURRENT_TIME=$(date "+%s")
COUNTER=$(expr ${CURRENT_TIME} - ${INITIAL_TIME})
if [[ ${COUNTER} -le "15" ]]; then
echo "counting ${COUNTER}"
# RUN YOUR COMMAND IN PLACE OF THE ABOVE echo COMMAND
elif [[ ${COUNTER} -gt "15" ]]; then
exit 0
#INITIATE YOUR KILL COMMAND IN PLACE OF OR BEFORE THE exit
else
echo "ERROR... YourScript.sh failed" >> /YourLogFile
fi
done

Related

How to wait in bash till a shell script is finished?

right now I'm using this script for a program:
export FREESURFER_HOME=$HOME/freesurfer
source $FREESURFER_HOME/SetUpFreeSurfer.sh
cd /home/ubuntu/fastsurfer
datadir=/home/ubuntu/moya/data
fastsurferdir=/home/ubuntu/moya/output
mkdir -p $fastsurferdir/logs # create log dir for storing nohup output log (optional)
while read p ; do
echo $p
nohup ./run_fastsurfer.sh --t1 $datadir/$p/orig.nii \
--parallel --threads 16 --sid $p --sd $fastsurferdir > $fastsurferdir/logs/out-${p}.log &
sleep 3600s
done < /home/ubuntu/moya/data/subjects-list.txt
Instead of using sleep 3600s, as the program needs around an hour, I'd like to use wait until all processes (several PIDS) are finished.
If this is the right way, can you tell me how to do that?
BR Alex
wait will wait for all background processes to finish (see help wait). So all you need is to run wait after creating all of the background processes.
This may be more than what you are asking for but I figured I would provide some methods for controlling the number of threads you want to have running at once. I find that I always want to limit the number for various reasons.
Explaination
The following will limit concurrent threads to max_threads running at one time. I am also using the main design pattern so we have a main that runs the script with a function run_jobs that handles the calling and waiting. I read all of $p into an array, then traverse that array as we launch threads. It will either launch a thread up to 4 or wait 5 seconds, once there are at least one less than four it will start another thread. When finished it waits for any remaining to be done. If you want something more simplistic I can do that as well.
#!/usr/bin/env bash
export FREESURFER_HOME=$HOME/freesurfer
source $FREESURFER_HOME/SetUpFreeSurfer.sh
typeset max_threads=4
typeset subjects_list="/home/ubuntu/moya/data/subjects-list.txt"
typeset subjectsArray
run_jobs() {
local child="$$"
local num_children=0
local i=0
while [[ 1 ]] ; do
num_children=$(ps --no-headers -o pid --ppid=$child | wc -w) ; ((num_children-=1))
echo "Children: $num_children"
if [[ ${num_children} -lt ${max_threads} ]] ;then
if [ $i -lt ${#subjectsArray[#]} ] ;then
((i+=1))
# RUN COMMAND HERE &
./run_fastsurfer.sh --t1 $datadir/${subjectsArray[$i]}/orig.nii \
--parallel --threads 16 --sid ${subjectsArray[$i]} --sd $fastsurferdir
fi
fi
sleep 10
done
wait
}
main() {
cd /home/ubuntu/fastsurfer
datadir=/home/ubuntu/moya/data
fastsurferdir=/home/ubuntu/moya/output
mkdir -p $fastsurferdir/logs # create log dir for storing nohup output log (optional)
mapfile -t subjectsArray < ${subjects_list}
run_jobs
}
main
Note: I did not run this code since you have not provided enough information to actually do so.

execute php file from shell script excluding certain time

I have an Shell script that runs a PHP script every 8 seconds as seen below:
enter code here
#!/bin/bash
while true; do
php /var/www/html/folder1/scripts/processtask.php /var/www/html/folder1/app
sleep 8;
done
enter code here
I need it to continue this way, however I need it to stop running at 2am until 3am then resume after that on a daily basis
You can use cronetab for the same. Check the below link for configuration:
https://www.baeldung.com/linux/schedule-script-execution
If you only want to start the script if not between 2am and 3am (but an allready started script does not need to be stopped) this could be easily checked with an additional if-statement:
#!/bin/bash
while true; do
if [[ 2 -le $(date '+%H') && $(date '+%H') -le 3 ]]; then
sleep 60
else
php /var/www/html/folder1/scripts/processtask.php /var/www/html/folder1/app
sleep 8
fi
done

Bash wait terminates immediately?

I want to play a sound after a command finishes, but only if the command took more than a second.
I have this code (copied from https://stackoverflow.com/a/11056286/1757964 and modified slightly):
( $COMMAND ) & PID=$!
( sleep 1; wait -f $PID 2>/dev/null; paplay "$SOUND" ) 2>/dev/null & WATCH=$!
wait -f $PID 2>/dev/null && pkill -HUP -P $WATCH
The "wait" on the second line seems to terminate immediately, though. I tried the -f flag but the behavior didn't change. How can I make the "wait" actually wait?
The problem is that you're running wait in a subshell. A shell can only wait for its own children, but the process you're trying to wait for is a sibling of the subshell, not a child.
There's no need to use wait for this. The question you copied the code from is for killing a process if it takes more than N seconds, not for telling how long a command took.
You can use the SECONDS variable to tell if it took more than a second. This variable contains the number of seconds since the shell started, so just check if it has increased.
start=$SECONDS
$COMMAND
if (($SECONDS > $start))
then
paplay "$SOUND"
fi
I'd probably want to streamline this to capture the data (in # of seconds since epoch) and then compare the difference (and if > 1 second then play sound), eg:
prgstart=$(date '+%s') # grab current time in terms of # of seconds since epoch
$COMMAND # run command in foreground => no need for sleep/wait/etc; once it completes ...
prgend=$(date '+$s') # grab current time in terms of # of seconds since epoch
if [[ $(( ${prgend} - ${prgstart} )) -gt 1 ]]
then
paplay "$SOUND"
fi

Waiting for a command to return in a bash script

What I am trying to do:
My bash shell script contains a modprobe -a $modulename. Sometimes loading that module fails and the modprobe statement just gets stuck. It never returns and hence, my script is stuck too.
What I want to do is this: Call modprobe -a $modulename , wait for 20 secs and if the command does not return and script remains stuck for 20 secs, call that a failure and exit !
I am looking at possible options for that. I know timeout is one, which will allow me to timeout after certain time. So I am thinking :
timeout -t 10 modprobe -a $modulename
if [ "$?" -gt 0 ]; then
echo "error"
exit
fi
But the problem is $? can be > 0 , not just because of timeout, but because of an error while loading the module too and I want to handle the two cases differently.
Any ideas using timeout and without using timeout are welcome.
According to timeout(1), timeout exits with a specific code (124 in my case) if the command times out. It's highly unlikely that modprobe would exit with that code, so you could probably check specifically for that by changing your condition:
...
RET="$?"; if [[ "$RET" = "124" ]]; then echo timeout; OTHER COMMAND; elif [[ "$RET" -gt 0 ]]; then echo error; exit; fi
BTW, it is a very good practice to assign "$?" to a variable immediately after your command. You will avoid a lot of grief later...
If you really do need to make sure, you can check the modprobe source code to see what exit codes it produces, since apparently it was not deemed important enough to mention in its manual page...
consider using "expect", you can set a timeout as well as running different command depending on the outcome of the modprobe.
Regards,
Andrew.

killall httpd for sleep process

this shell explain the issue ,
after executing the .sh file halt and nothing happen , any clue where is my mistake
its kill httpd if there is more than 10 sleep process and start the httpd with zero sleep process
#!/bin/bash
#this means loop forever
while [ 1 ];
do HTTP=`ps auwxf | grep httpd | grep -v grep | wc -l`;
#the above line counts the number of httpd processes found running
#and the following line says if there were less then 10 found running
if [ $[HTTP] -lt 10 ];
then killall -9 httpd;
#inside the if now, so there are less then 10, kill them all and wait 1 second
sleep 1;
#start apache
/etc/init.d/httpd start;
fi;
#all done, sleep for ten seconds before we loop again
sleep 10;done
Why would you kill the child processes? If you do that you killing all ongoing sessions. Would it not be easier to setup your Webserver configuration so that it matches your needs?
As Dennis has mentioned already your script should look like:
#!/bin/bash
BINNAME=httpd # Name of the process
TIMEOUT=10 # Seconds to wait until next loop
MAXPROC=10 # Maximum amount of procs for given daemon
while true
do
# Count number of procs
HTTP=`pgrep $BINNAME | wc -l`
# Check if more then $MAXPROC are running
if [ "$HTTP" -gt "$MAXPROC" ]
then
# Kill the procs
killall-9 $BINNAME
sleep 1
# start http again
/etc/init.d/httpd start
fi
sleep $TIMEOUT
done
Formating makes code more readable ;)
I can't see anything wrong with it.
This line:
if [ $[HTTP] -lt 10 ];
should probably be:
if [ ${HTTP} -lt 10 ];
even though yours works.
If you add this as the last line, you should never see its output since you're in an infinite while loop.
echo "At end"
If you do, then that's really weird.
Make your first line look like this and it will display the script line-by-line as it executes to help you see where it's going wrong:
#!/bin/bash -x
Watch out for killall if you are trying to write portable scripts. It doesn't mean the same thing on every system: while on linux it means "kill processes named like this" on some systems it means "kill every process I have permission to kill".
If you run the later version as root, one of the things you kill is init. Oops.

Resources