Sleep until a specific time/date

前端 未结 18 1966
栀梦
栀梦 2020-11-28 21:13

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

The \"at\"-d

相关标签:
18条回答
  • 2020-11-28 21:20

    Here is a solution that does the job AND informs the user about how much time is remaining. I use it almost everyday to run scripts during the night (using cygwin, as I couldn't get cron to work on windows)

    Features

    • Precise down to the second
    • Detects system time changes and adapts
    • Intelligent output telling how much time is left
    • 24-hour input format
    • returns true to be able to chain with &&

    Sample run

    $ til 13:00 && date
    1 hour and 18 minutes and 26 seconds left...
    1 hour and 18 minutes left...
    1 hour and 17 minutes left...
    1 hour and 16 minutes left...
    1 hour and 15 minutes left...
    1 hour and 14 minutes left...
    1 hour and 10 minutes left...
    1 hour and  5 minutes left...
    1 hour and  0 minutes left...
    55 minutes left...
    50 minutes left...
    45 minutes left...
    40 minutes left...
    35 minutes left...
    30 minutes left...
    25 minutes left...
    20 minutes left...
    15 minutes left...
    10 minutes left...
     5 minutes left...
     4 minutes left...
     3 minutes left...
     2 minutes left...
     1 minute left...
    Mon, May 18, 2015  1:00:00 PM
    

    (The date at the end is not part of the function, but due to the && date)

    Code

    til(){
      local hour mins target now left initial sleft correction m sec h hm hs ms ss showSeconds toSleep
      showSeconds=true
      [[ $1 =~ ([0-9][0-9]):([0-9][0-9]) ]] || { echo >&2 "USAGE: til HH:MM"; return 1; }
      hour=${BASH_REMATCH[1]} mins=${BASH_REMATCH[2]}
      target=$(date +%s -d "$hour:$mins") || return 1
      now=$(date +%s)
      (( target > now )) || target=$(date +%s -d "tomorrow $hour:$mins")
      left=$((target - now))
      initial=$left
      while (( left > 0 )); do
        if (( initial - left < 300 )) || (( left < 300 )) || [[ ${left: -2} == 00 ]]; then
          # We enter this condition:
          # - once every 5 minutes
          # - every minute for 5 minutes after the start
          # - every minute for 5 minutes before the end
          # Here, we will print how much time is left, and re-synchronize the clock
    
          hs= ms= ss=
          m=$((left/60)) sec=$((left%60)) # minutes and seconds left
          h=$((m/60)) hm=$((m%60)) # hours and minutes left
    
          # Re-synchronise
          now=$(date +%s) sleft=$((target - now)) # recalculate time left, multiple 60s sleeps and date calls have some overhead.
          correction=$((sleft-left))
          if (( ${correction#-} > 59 )); then
            echo "System time change detected..."
            (( sleft <= 0 )) && return # terminating as the desired time passed already
            til "$1" && return # resuming the timer anew with the new time
          fi
    
          # plural calculations
          (( sec > 1 )) && ss=s
          (( hm != 1 )) && ms=s
          (( h > 1 )) && hs=s
    
          (( h > 0 )) && printf %s "$h hour$hs and "
          (( h > 0 || hm > 0 )) && printf '%2d %s' "$hm" "minute$ms"
          if [[ $showSeconds ]]; then
            showSeconds=
            (( h > 0 || hm > 0 )) && (( sec > 0 )) && printf %s " and "
            (( sec > 0 )) && printf %s "$sec second$ss"
            echo " left..."
            (( sec > 0 )) && sleep "$sec" && left=$((left-sec)) && continue
          else
            echo " left..."
          fi
        fi
        left=$((left-60))
        sleep "$((60+correction))"
        correction=0
      done
    }
    
    0 讨论(0)
  • 2020-11-28 21:20

    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.%N)
    target=$(date -d "$1" +%s.%N)
    
    seconds=$(echo "scale=9; $target - $current" | bc)
    
    signchar=${seconds:0:1}
    if [ "$signchar" = "-" ]; 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
    
    0 讨论(0)
  • 2020-11-28 21:21
     function sleepuntil() {
      local target_time="$1"
      today=$(date +"%m/%d/%Y")
      current_epoch=$(date +%s)
      target_epoch=$(date -d "$today $target_time" +%s)
      sleep_seconds=$(( $target_epoch - $current_epoch ))
    
      sleep $sleep_seconds
    }
    
    target_time="11:59"; sleepuntil $target_time
    
    0 讨论(0)
  • 2020-11-28 21:23

    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!

    0 讨论(0)
  • 2020-11-28 21:28

    To extend the main answer, here is some valid examples regarding the date string manipulation:

    sleep $(($(date -f - +%s- <<< $'+3 seconds\nnow')0)) && ls
    
    sleep $(($(date -f - +%s- <<< $'3 seconds\nnow')0)) && ls
    
    sleep $(($(date -f - +%s- <<< $'3 second\nnow')0)) && ls
    
    sleep $(($(date -f - +%s- <<< $'+2 minute\nnow')0)) && ls
    
    sleep $(($(date -f - +%s- <<< $'tomorrow\nnow')0)) && ls
    
    sleep $(($(date -f - +%s- <<< $'tomorrow 21:30\nnow')0)) && ls
    
    sleep $(($(date -f - +%s- <<< $'3 weeks\nnow')0)) && ls
    
    sleep $(($(date -f - +%s- <<< $'3 week\nnow')0)) && ls
    
    sleep $(($(date -f - +%s- <<< $'next Friday 09:00\nnow')0)) && ls
    
    sleep $(($(date -f - +%s- <<< $'2027-01-01 00:00:01 UTC +5 hours\nnow')0)) && ls
    
    0 讨论(0)
  • 2020-11-28 21:30

    Use tarry. It's a tool I wrote to specifically do this.

    https://github.com/metaphyze/tarry/

    This is a simple command line tool for waiting until a specific time. This is not the same as "sleep" which will wait for a duration of time. This is useful if you want to execute something at a specific time or more likely execute several things at exactly the same time such as testing if a server can handle multiple very simultaneous requests. You could use it like this with "&&" on Linux, Mac, or Windows:

       tarry -until=16:03:04 && someOtherCommand
    

    This would wait until 4:03:04 PM and then execute someOtherCommand. Here's a Linux/Mac example of how to run multiple requests all scheduled to start at the same time:

       for request in 1 2 3 4 5 6 7 8 9 10
       do
           tarry -until=16:03:04 && date > results.$request &
       done
    

    Ubuntu, Linux, and Windows binaries are available through links on the page.

    0 讨论(0)
提交回复
热议问题