13

I need a series of commands or a single command that sleeps until the next occurrence of a specific time like "4:00" occurs.

How would I do that?

The at command or a cronjob is not a option because I must not leave the script I'm currently in.

The specific case I am talking about is a script running in screen. It is very important that I do not stop the execution of the script by the script itself because there are store many important variables which are needed at some point of the script. The script is not always supposed to be executed in a regular matter. It just needs to be executed at a specific time.

It would be very benificial if the script would have to create any files or any other tasks such as cronjobs or other screens. This is simply a question of design.

I just had an awsome idea:

difference=$(($(date -d "4:00" +%s) - $(date +%s)))

if [ $difference -lt 0 ]
then
    sleep $((86400 + difference))
else
    sleep $difference
fi

Do you have any better ideas?

More information will be added if requested!

3
  • Please could you clarify a bit your question? What do you mean when you say: "I must not leave the script I'm currently in"
    – sebelk
    Oct 10, 2013 at 21:23
  • How about you have your script check for the existence of a "wake up" file and sleep for a period of time before checking again? Then you can use cron to create the file, leveraging cron's reliability, but you can have your long-lived script too. Just have the script remove the "wake up" file when it stops sleeping.
    – kurtm
    Oct 10, 2013 at 21:23
  • 1
    Note that on a Mac you should replace date -d with date -j and replace 4:00 with 0400. Jun 4, 2016 at 8:48

3 Answers 3

22

terdon's suggestion would work but I guess mine is more efficient.

difference=$(($(date -d "4:00" +%s) - $(date +%s)))

if [ $difference -lt 0 ]
then
    sleep $((86400 + difference))
else
    sleep $difference
fi

This is calculating the difference between the given time and the current time in seconds. If the number is negative we have to add the seconds for a whole day (86400 to be exact) to get the seconds we have to sleep and if the number is postive we can just use it.

1
  • What happens if the system clock moves forwards/backwards (eg: ntp).
    – WhyNotHugo
    Feb 27, 2017 at 12:41
12

Assuming it is a shell script, this should work:

while [ $(date +%H:%M) != "04:00" ]; do sleep 1; done

That's for 24 hour times. If you want this to continue both at 4:00 AM and 4:00 PM, use this instead:

while [ $(date +%I:%M) != "04:00" ]; do sleep 1; done
1
  • This might be a option. I just had an idea. I will be back in a few minutes.
    – BrainStone
    Oct 10, 2013 at 21:34
0

On OpenBSD, the following could be used to compact a */5 5-minute crontab(5) job into an 00 hourly one (to make sure fewer emails are generated, all whilst performing the same task at exact intervals):

#!/bin/sh -x
for k in $(jot 12 00 55)
  do
  echo $(date) doing stuff
  sleep $(expr $(date -j +%s $(printf %02d $(expr $k + 5))) - $(date -j +%s))
done

Note that the date(1) would also break the sleep(1) by design on the final iteration, as 60 minutes is not a valid time (unless it is!), thus we won't have to wait any extra time prior to getting our email report.

Also note that should one of the iterations take more than 5 minutes allotted to it, the sleep would likewise graciously fail by design by not sleeping at all (due to what is a negative number interpreted as a command-line option, instead of wrapping around to the next hour or even eternity), thus making sure your job could still complete within the hour allotted (e.g., if only one of the iterations takes a little bit more than 5 minutes, then we would still have the time to catch up, without anything wrapping around to the next hour).

The printf(1) is needed because date expects exactly two digits for the minute specification.

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .