How to stop infinite loop in bash script gracefull

2019-04-22 22:02发布

问题:

I need to run application in every X seconds, so, as far as cron does not work with seconds this way, I wrote a bash script with infinite loop having X seconds sleep in it.

When I have to stop the running script manually, I would like to do it in a correct way - let the application complete functioning and just do not enter the loop for the next time.

Do you have any idea, how this can be achieved? I thought about passing arguments, but I could not find how to pass argument to running script.

回答1:

You could trap a signal, say SIGUSR1:

echo "My pid is: $$"
finish=0
trap 'finish=1' SIGUSR1

while (( finish != 1 ))
do
    stuff
    sleep 42
done

Then, when you want to exit the loop at the next iteration:

kill -SIGUSR1 pid

Where pid is the process-id of the script. If the signal is raised during the sleep, it will wake (sleep sleeps until any signal occurs).



回答2:

You may pass some arguments by a file. On each iteration you may read this file and see if your running condtions got changed.



回答3:

The following structure may be appropriate if you have to do some cleanup anyway. Use kill as shown in cdarke's answer.

#===============================================================
#  FUNCTION DEFINITIONS
#===============================================================
# . . .

#===  FUNCTION  ================================================
#          NAME:  cleanup
#   DESCRIPTION:  
#===============================================================
cleanup ()
{
  # wait for active children, remove empty logfile, ..., exit
  exit 0
} # ----------  end of function cleanup  ----------

#===============================================================
#   TRAPS
#===============================================================
trap  cleanup SIGUSR1

#===============================================================
#   MAIN SCRIPT
#===============================================================
echo -e "Script '$0' / PID ${$}"

while : ; do                                # infinite loop
  # . . .
  sleep 10
done
# . . .

#===============================================================
#   STATISTICS / CLEANUP
#===============================================================
cleanup


回答4:

The trap solution posted earlier is good for large loops, but cumbersome for the common case where you are just looping over a single or a few commands. In this case I recommend the following solution:

while whatever; do
    command || break
done

This will exit the loop if command has a non-zero exit code, which will happen if it is interrupted. So unless command handles SIGINT, pressing ctrl-C will both stop the current command and exit the loop.

Edit: After reading your question more closely, I see that you want the current command to continue executing. In that case this solution does not apply.