tags:

views:

9787

answers:

8

I want my bash-script to sleep until a specific time. So I want a command like "sleep" which takes no interval but a end-time and sleeps until then.

The "at"-deamon is no solution as I need to block a running script until a date/time.

Is there such a command?

+1  A: 

You can calculate the number of seconds between now and the wake-up time and use the existing 'sleep' command.

Outlaw Programmer
+4  A: 

Use sleep, but compute the time using date. You'll want to use date -d for this. For example, let's say you wanted to wait until next week:

expr `date -d "next week" +%s` - `date -d "now" +%s`

Just substitute "next week" with whatever date you'd like to wait for, then assign this expression to a value, and sleep for that many seconds:

startTime = $(date +%s)
endTime = $(date -d "next week" +%s)
timeToWait = $(( $end - $start ))
sleep $timeToWait

All done!

John Feminella
It's worth expanding on how you'd assign the value to a variable, since timeToWait=expr ... won't work directly, and you can't use backticks because they won't nest, so you'll have to use $(), or temporary variables.
SpoonMeiser
Good suggestion; I'll modify mine to make that clearer so that people don't get the wrong idea.
John Feminella
+12  A: 

As mentioned by Outlaw Programmer, I think the solution is just to sleep for the correct number of seconds.

To do this in bash, do the following:

current_epoch=$(date +%s)
target_epoch=$(date -d '01/01/2010 12:00' +%s)

sleep_seconds=$(( $target_epoch - $current_epoch ))

sleep $sleep_seconds
SpoonMeiser
Thanks for that, I wrote a small shell-script to get a "sleepuntil" command.
theomega
A: 

You could perhaps use 'at' to send a signal to your script, which sat waiting for that signal.

Kim Reece
Aww man, I was just writing up another answer to this effect.
SpoonMeiser
Well, actually, no, mine was slightly different.
SpoonMeiser
Yours is cooler. =)
Kim Reece
+3  A: 

You can stop a process from executing, by sending it a SIGSTOP signal, and then get it to resume executing by sending it a SIGCONT signal.

So you could stop your script by sending is a SIGSTOP:

kill -SIGSTOP <pid>

And then use the at deamon to wake it up by sending it a SIGCONT in the same way.

Presumably, your script will inform at of when it wanted to be woken up before putting itself to sleep.

SpoonMeiser
+2  A: 

To follow on SpoonMeiser's answer, here's a specific example:

$cat ./reviveself

#!/bin/bash

# save my process ID
rspid=$$

# schedule my own resuscitation
# /bin/sh seems to dislike the SIGCONT form, so I use CONT
# at can accept specific dates and times as well as relative ones
# you can even do something like "at thursday" which would occur on a 
# multiple of 24 hours rather than the beginning of the day
echo "kill -CONT $rspid"|at now + 2 minutes

# knock myself unconscious
# bash is happy with symbolic signals
kill -SIGSTOP $rspid

# do something to prove I'm alive
date>>reviveself.out
$
Dennis Williamson
I think you want to schedule a SIGCONT, rather than another SIGSTOP, so either the signal, or the comment is wrong. Otherwise, nice to see a proper example.
SpoonMeiser
Oops, I typo'd the comment. Now fixed. But you have to watch using numeric signals, since they can be different.
Dennis Williamson
I found that dash seems to not understand SIGCONT, so at first I used -18 which is non-portable, then I found that it likes CONT, so I edited the answer above to fix that.
Dennis Williamson
A: 

timeToWait = $(( $end - $start ))

Beware that "timeToWait" could be a negative number! (for example, if you specify to sleep until "15:57" and now it's "15:58"). So you have to check it to avoid strange message errors:

#!/bin/bash
set -o nounset

### // Sleep until some date/time. 
# // Example: sleepuntil 15:57; kdialog --msgbox "Backup needs to be done."


error() {
  echo "$@" >&2
  exit 1;
}

NAME_PROGRAM=$(basename "$0")

if [[ $# != 1 ]]; then
     error "ERROR: program \"$NAME_PROGRAM\" needs 1 parameter and it has received: $#." 
fi


CURRENT=$(date +%s)
TARGET=$(date -d "$1" +%s)

SECONDS=$(($TARGET - $CURRENT))

if [[ $SECONDS < 0 ]]; then
     error "You need to specify in a different way the moment in which this program has to finish, probably indicating the day and the hour like in this example: $NAME_PROGRAM \"2009/12/30 10:57\"."
fi

sleep "$SECONDS"

# // End of file

Excuse my English :-). Greetings from Spain!

A: 

I wanted an script that only checked the hours and minutes so I could run the script with the same parameters every day. I don't want to worry about which day will be tomorrow. So I used a different approach.

target="$1.$2"
cur=$(date '+%H.%M')
while test $target != $cur; do
    sleep 59
    cur=$(date '+%H.%M')
done

the parameters to the script are the hours and minutes, so I can write something like:

til 7 45 && mplayer song.ogg

(til is the name of the script)

no more days late at work cause you mistyped the day. cheers!

kolomer