Use the sleep command.
Example:
sleep .5 # Waits 0.5 second.
sleep 5 # Waits 5 seconds.
sleep 5s # Waits 5 seconds.
sleep 5m # Waits 5 minutes.
sleep 5h # Waits 5 hours.
sleep 5d # Waits 5 days.
One can also employ decimals when specifying a time unit; e.g. sleep 1.5s
Use the sleep command.
Example:
sleep .5 # Waits 0.5 second.
sleep 5 # Waits 5 seconds.
sleep 5s # Waits 5 seconds.
sleep 5m # Waits 5 minutes.
sleep 5h # Waits 5 hours.
sleep 5d # Waits 5 days.
One can also employ decimals when specifying a time unit; e.g. sleep 1.5s
For those looking for the Bash equivalent of Windows Powershell/CMD's pause command.
In Bash use read with option -p specifying a prompt like:
read -p "Press enter to continue"
The error is because & is being passed to the /bin/sleep command as an argument, whereas you are using it in the context of telling the shell to execute the command in the background.
If you want to use '&' or '&&' you can use os.system:
os.system('sleep 1 && echo foo && sleep 5 && echo bar')
or to run nonblocking (in the background) you can use the standard &:
os.system('sleep 1 && echo foo && sleep 5 && echo bar &')
You can't do what you are wanting to do, force a python script to run in the background every time. Python also can't run a task directly in the background. You might look into Threading, which might accomplish what you want, but without knowing why you want to go back to the command prompt, it's difficult. I think what you might want is a threading timer specifically, an example of which can be found here.
from threading import Timer
def hello():
print "hello, world"
t = Timer(30.0, hello)
t.start() # after 30 seconds, "hello, world" will be printed
This code can help. It runs the python script in a random minute between the interval you want to. Also this bash script should be added to crontab file to be scheduled at 02.00PM.
#!/bin/bash
maxtime=$((4*60*60))
delay=
RANDOM%maxtime))
(sleep $((delay)); /usr/bin/python /path/to/yourscript.py) & #background the sleep process, then run your script
You could use cron to run the bash script @1400, and the bash script would have a random number of minutes to sleep less than 240 (4 hours * 60 minutes). When sleep runs out, call the python.
I assume you know about $RANDOM?
#!/bin/bash
sleep
RANDOM % 240 ))m
./mypython.py
To account for new constraints in the edit:
#!/bin/bash
sleep
RANDOM % 240))m
sleep
RANDOM % 60))
./mypython.py
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
To add precision down to nanoseconds (effectively more around milliseconds) use e.g. this syntax:
current_epoch=$(date +%s.%N)
target_epoch=$(date -d "20:25:00.12345" +%s.%N)
sleep_seconds=$(echo "$target_epoch - $current_epoch"|bc)
sleep $sleep_seconds
Note that macOS / OS X does not support precision below seconds, you would need to use coreutils from brew instead → see these instructions
Sample edited: Wed Apr 22 2020, something between 10:30 and 10h:55
(Important for reading samples)
0. Preamble
In this not so short answer, I will present
- Avoiding useless forks!
- POSIX shell compatible
- High precision (nanosec)
- Reaching next time
- Pure bash way, no fork!!
- As a function
- HiRes time with bash
- sleep until next period
Old method using/proc/timer_listunder GNU/Linux- Little test function
- What if host do hibernate during
sleepcommand?- Test on my laptop
- Care to not mix
$EPOCHSECONDand$EPOCHREALTIME - About
sleep <float>vsread -t <float> _- Some tests (on my raspberry)
- Using bash'
sleeploadable module
To avoid consecutive forks, this method use GNU date -f which is not POSIX and don't work under MacOS! If under Mac, goto my pure bash function or install GNU date!
1. Simple starting sample POSIX shell compatible
In order to reduce forks, instead of running date two times, I prefer to use this:
sleep $(( $( printf '%s\nnow\n' "tomorrow 21:30" | date -f - +%s- ) 0 ))
where tomorrow 21:30 could be replaced by any kind of date format recognized by date command, ( in the future of course ;) .
if read -rp "Sleep until: " targetTime ;then
sleep $(( $( printf '%s\nnow\n' "$targetTime" | date -f - +%s- ) 0 ))
fi
1.1. With high precision (nanosec)
Nearly same:
target='07:00 tomorrow'
sleep $(echo s$( printf '%s\nnow\n' "$target"| date -f - +'t=%s.%N;')st-t | bc)
Verbose:
target='07:00 tomorrow'
echo sleep $(echo s$( printf '%s\nnow\n' "$target" | date -f - +'t=%s.%N;')st-t|
bc ) | tee /dev/tty | sh
1.2. Reaching next time
For reaching next HH:MM meaning today if possible, tomorrow if too late:
sleep $(( ( $( printf 'tomorrow %s\nnow\n' 21:30 | date -f - +%s-) 0 ) % 86400 ))
Under bash, ksh and some other modern shells, you could write:
sleep $(( ( $( date -f - +%s- <<<"21:30"$' tomorrow\nnow' ) 0 ) % 86400 ))
2. Pure bash way, no fork!!
Tested under MacOS!
Under bash, you could use printf -v now '%(%s)T' -1 for storing current UNIXEPOCH in variable $now.
2.1. As a function
I wrote one two little functions: sleepUntil and sleepUntilHires
Syntax:
sleepUntil [-q] <HH[:MM[:SS]]> [more days]
-q Quiet: don't print sleep computed argument
HH Hours (minimal required argument)
MM Minutes (00 if not set)
SS Seconds (00 if not set)
more days multiplied by 86400 (0 by default)
To sleep until HH:MM whithout using date or any other fork, I've build a little bash function. Here it is:
sleepUntil() { # args: [-q] <HH[:MM[:SS]]> [more days]
local slp tzoff now quiet=false
[ "$1" = "-q" ] && shift && quiet=true
local -a hms=(${1//:/ })
printf -v now '%(%s)T' -1
printf -v tzoff '%(%z)T' $now
tzoff=$((0${tzoff:0:1}(3600*10#${tzoff:1:2}+60*10#${tzoff: -2})))
slp=$(((( 86400 + ( now - now%86400 ) +
10#$hms*3600+10#${hms[1]:-0}*60+10#${hms[2]:-0} -
tzoff - now
) % 86400 ) + 10#${2:-0} * 86400
))
$quiet || printf 'sleep %ss, -> %(%c)T\n' $slp $((now+slp))
read -t $slp _
}
Then:
sleepUntil 10:37 ; date +"Now, it is: %T"
sleep 49s, -> Wed Apr 22 10:37:00 2020
Now, it is: 10:37:00
sleepUntil -q 10:37:44 ; date +"Now, it is: %T"
Now, it is: 10:37:44
sleepUntil 10:50 1 ; date +"Now, it is: %T"
sleep 86675s, -> Thu Apr 23 10:50:00 2020
^C
If target is before this will sleep until tomorrow:
sleepUntil 10:30 ; date +"Now, it is: %T"
sleep 85417s, -> Thu Apr 23 10:30:00 2020
^C
sleepUntil 10:30 1 ; date +"Now, it is: %T"
sleep 171825s, -> Fri Apr 24 10:30:00 2020
^C
3. HiRes time with bash
Recent bash, from version 5.0 add new $EPOCHREALTIME variable with microseconds.
From this there is a sleepUntilHires function:
sleepUntilHires() { # args: [-q] <HH[:MM[:SS[.xxx]]]> [more days]
local slp tzoff now quiet=false musec musleep tmu
[ "$1" = "-q" ] && shift && quiet=true
local -a hms
IFS=: read -a hms <<<${1}
printf -v tmu %.06f ${hms[2]:-0}
printf -v hms[2] %.0f ${tmu%.*}
tmu=${tmu#*.}
printf -v now '%(%s)T' -1
IFS=. read now musec <<<$EPOCHREALTIME
musleep=$((2000000+10#$tmu-10#$musec))
printf -v tzoff '%(%z)T\n' $now
tzoff=$((0${tzoff:0:1}(3600*10#${tzoff:1:2}+60*10#${tzoff:3:2})))
slp=$(((( 86400 + ( now - now%86400 ) +
10#$hms*3600+10#0${hms[1]:-0}*60+10#${hms[2]:-0} -
tzoff - now - 1
) % 86400 ) + 10#${2:-0} * 86400
)).${musleep:1}
$quiet ||
printf 'sleep %ss, -> %(%c)T.%s\n' $slp $((now+${slp%.*}+1)) $tmu
read -t $slp _
}
Please note: this use read -t wich is built-in, instead of sleep. Unfortunely, this won't work when running in background, without real TTY. Feel free to replace read -t by sleep if you
plan to run this in background scripts... (But for background process, consider using cron and/or at instead of all this)
3.1. HiRes sleep until next period
About RonJohn's comment, if the goal is to start every 15 minutes, sleep command could become:
sleepUntilNext() { # args: [-q] <float period in seconds>
local slp sec mus quiet=0 res=0 chr
[[ $1 == -q ]] && quiet=1 && shift
printf -v slp %.6f $1
slp=00000$(( 10#${slp/.} - ${EPOCHREALTIME/.} % 10#${slp/.} ))
printf -v slp %.6f ${slp::-6}.${slp: -6}
((quiet)) || printf 'Sleep %s...' $slp >&2
IFS= read -d '' -rsn1 -t $slp chr &&
printf -v res %d \'"$chr"
IFS=. read sec mus <<<$EPOCHREALTIME
((quiet)) || {
IFS=. read sec mus <<<$EPOCHREALTIME
printf '\rDone: %(%a %d %T)T.%s\e[K\n' $sec $mus >&2
}
return $res
}
Then for a period of 15':
sleepUntilNext 900
Sleep 662.334231...
Done: Wed 26 16:15:00.000171
for 20':
sleepUntilNext 1200
Sleep 11.509587...
Done: Wed 26 16:20:00.000168
But this accept float values as period:
while sleepUntilNext -q 1.5;do date +%T.%N;done
16:32:49.502416682
16:32:51.001073971
16:32:52.502804292
16:32:54.002940518
16:32:55.503174242
(Replace read -t by sleep or use pseudo FD... see About sleep and/or read -t further)
Skip next paragraph for tests and warning about $ËPOCHSECONDS!
3.2. Older method using /proc/timer_list under GNU/Linux
Avoided to normal user, by recent Kernel!! Do require root user
Under Linux kernel, you will find a variables file named /proc/timer_list where you could read an offset and a now variable, in nanoseconds. So we may compute sleep time to reach the very top desired time.
mapfile </proc/timer_list _timer_list
for ((_i=0;_i<${#_timer_list[@]};_i++));do
[[ ${_timer_list[_i]} =~ ^now ]] && TIMER_LIST_SKIP=$_i
[[ ${_timer_list[_i]} =~ offset:.*[1-9] ]] && \
TIMER_LIST_OFFSET=${_timer_list[_i]//[a-z.: ]} && \
break
done
unset _i _timer_list
readonly TIMER_LIST_OFFSET TIMER_LIST_SKIP
sleepUntilHires() {
local slp tzoff now quiet=false nsnow nsslp
[ "$1" = "-q" ] && shift && quiet=true
local hms=(${1//:/ })
mapfile -n 1 -s $TIMER_LIST_SKIP nsnow </proc/timer_list
printf -v now '%(%s)T' -1
printf -v tzoff '%(%z)T\n' $now
nsnow=$((${nsnow//[a-z ]}+TIMER_LIST_OFFSET))
nsslp=$((2000000000-10#${nsnow:${#nsnow}-9}))
tzoff=$((0${tzoff:0:1}(3600*${tzoff:1:2}+60*${tzoff:3:2})))
slp=$(( ( 86400 + ( now - now%86400 ) +
10#$hms*3600+10#${hms[1]}*60+${hms[2]} -
tzoff - now - 1
) % 86400)).${nsslp:1}
$quiet || printf 'sleep %ss, -> %(%c)T\n' $slp $((now+${slp%.*}+1))
sleep $slp
}
After defining two read-only variables, TIMER_LIST_OFFSET and TIMER_LIST_SKIP, the function will access very quickly the variable file /proc/timer_list for computing sleep time:
3.3. Little test function
Showing near begin and end of every seconds, by sleeping 0,92 between two outputs of same HMS...
tstSleepUntilHires () {
local now next last
printf -v next "%(%H:%M:%S)T" $((${EPOCHREALTIME%.*}+1))
sleepUntilHires $next
date -f - +%F-%T.%N < <(echo now;sleep .92;echo now)
printf -v next "%(%H:%M:%S)T" $((${EPOCHREALTIME%.*}+1))
sleepUntilHires $next
date +%F-%T.%N
}
May render something like:
sleep 0.244040s, -> Wed Apr 22 10:34:39 2020.000000
2020-04-22-10:34:39.001685312
2020-04-22-10:34:39.922291769
sleep 0.077012s, -> Wed Apr 22 10:34:40 2020.000000
2020-04-22-10:34:40.004264869
- At begin of next second,
- print time, then
- wait 0.92 seccond, then
- print time, then
- compute 0.07 seconds left, to next second
- sleep 0.07 seconds, then
- print time.
4. What if host do hibernate during sleep command?
Jan 2023: New chapter about GregD's comment and hibernation
GregD's comment warned about too long sleep if initiated before computer going into deep sleep mode...
For this I've rewrited sleepUntilHires: Of course, they won't wake computer up, but end immediately (max 20 ms) when waked and show time gap.
sleepUntilHires() { # args: [-q] <HH[:MM[:SS[.xxx]]]> [more days]
local slp tzoff now quiet=false musec musleep tmu start=${EPOCHREALTIME} tgt
[[ $_dummySleepFd ]] && [[ -e /dev/fd/$_dummySleepFd ]] ||
exec {_dummySleepFd}<> <(:)
[ "$1" = "-q" ] && shift && quiet=true
local -a hms; IFS=: read -a hms <<<${1}
printf -v tmu %.06f ${hms[2]:-0}
printf -v hms[2] %.0f ${tmu%.*}
tmu=${tmu#*.}; printf -v now '%(%s)T' -1
IFS=. read now musec <<<$start
musleep=$((2000000+10#$tmu-10#$musec))
printf -v tzoff '%(%z)T\n' $now
tzoff=$((0${tzoff:0:1}(3600*10#${tzoff:1:2}+60*10#${tzoff:3:2})))
slp=$(((( 86400 + ( now - now%86400 ) +
10#$hms*3600+10#0${hms[1]:-0}*60+10#${hms[2]:-0} -
tzoff - now - 1
) % 86400 ) + 10#${2:-0} * 86400
)).${musleep:1}
tgt=$((${start/.}+10#${slp/.}))
printf -v tmu '%(%c)T.%s' $(((${start/.}+10#${slp/.})/1000000)) $tmu
while ((slp=tgt-${EPOCHREALTIME/.},slp>0)) ;do
slp=00000$slp
printf -v slp %.6f ${slp::-6}.${slp: -6}
$quiet || printf '\rsleep %ss, -> %s ...' "$slp" "$tmu"
if (( 10#${slp/.} > 20000));then # If left more than 20 ms
read -u $_dummySleepFd -t .02 _ # then sleep 20 ms
else read -u $_dummySleepFd -t $slp _ # else sleep left time
fi
done
if ! $quiet;then now=00000$((${EPOCHREALTIME/.}-tgt))
printf '\nScript done %.6f seconds late.\n' ${now::-6}.${now: -6}
fi
}
4.1. Test on my laptop
Well I have to start my laptop now...
sleepUntilHires 15:13:18.1234
sleep 0.002148s, -> lun 16 jan 2023 15:13:18 CET.123400 ...
Script done 0.000746 seconds late.
That was a normal run. Now I will put my laptop in hibernation:
sleepUntilHires 15:15:18.1234
sleep 86.256328s, -> lun 16 jan 2023 15:15:18 CET.123400 ...
Script done 111.469551 seconds late.
Then sleep finish immediately when waked up, and show his final gap.
5. Care to not mix $EPOCHSECOND and $EPOCHREALTIME!
Read my warning about difference between $EPOCHSECOND and $EPOCHREALTIME
This function use $EPOCHREALTIME so don't use $EPOCHSECOND for establishing next second:
Sample issue: Trying to print time next rounded by 2 seconds:
for i in 1 2;do
printf -v nextEvenSecond "%(%T)T" $(((EPOCHSECONDS/2)*2+2))
echo $nextEvenSecond
sleepUntilHires $nextEvenSecond
IFS=. read now musec <<<$EPOCHREALTIME
printf "%(%c)T.%s\n" $now $musec
done
May produce:
11:44:32
sleep 1.485212s, -> Wed Nov 9 11:44:32 2022.000000
Wed Nov 9 11:44:32 2022.000311
11:44:32
sleep 86399.999143s, -> Thu Nov 10 11:44:32 2022.000000
You are going to wait 1 day instead of 2 seconds!!!
You have to use $EPOCHREALTIME:
printf -v nextEvenSecond "%(%T)T" $(((${EPOCHREALTIME%.*}/2)*2+2))
11:48:12
sleep 0.300672s, -> Wed Nov 9 11:48:12 2022.000000
Wed Nov 9 11:48:12 2022.000345
11:48:14
sleep 1.998397s, -> Wed Nov 9 11:48:14 2022.000000
Wed Nov 9 11:48:14 2022.000536
11:48:16
sleep 1.998916s, -> Wed Nov 9 11:48:16 2022.000000
Wed Nov 9 11:48:16 2022.000325
6. About sleep and/or read -t
In this functions, I use read -t (timeout) instead of sleep, for two reasons:
- this permit user to interrupt sleep by hitting Return key.
(Variant:IFS= read -sn 1 -t $slp _, to permit user to interrupt by hitting Any key). sleepimplie a call (fork) to/bin/sleepas it's not a buitin.
For avoiding user interrupt by keyboard interaction aka for running this non interactively, you could
- create a pseudo tty (file descriptor) for redirecting
read's input at begin ofsleepUntil.sourcescript:
exec {_dummySleepFd}<> <(:)
then replace
read -t $slp _
by
read -u $_dummySleepFd -t $slp _
- Load
sleeploadable builtin if your bash implementation permit this (seesudo apt install bash-builtins):
enable -f sleep sleep
if your $BASH_LOADABLES_PATH is set, else:
enable -f /usr/lib/bash/sleep sleep
to unload/disable:
enable -d sleep
6.1. some tests (on my raspberry):
Command sleep 0.01 runned between two print of UNIXEPOCH in microseconds could print something greater (or equal) to 10000 microseconds...
By using read -t instead of sleep:
exec {_dummySleepFd}<> <(:)
s=${EPOCHREALTIME/.};read -u $_dummySleepFd -t .01;echo $((${EPOCHREALTIME/.}-s))
10975
s=${EPOCHREALTIME/.};read -u $_dummySleepFd -t .01;echo $((${EPOCHREALTIME/.}-s))
10977
Near 1000 microseconds -> 1 milliseconds late.
With /bin/sleep:
s=${EPOCHREALTIME/.};sleep .01;echo $((${EPOCHREALTIME/.}-s))
60676
s=${EPOCHREALTIME/.};sleep .01;echo $((${EPOCHREALTIME/.}-s))
47910
s=${EPOCHREALTIME/.};sleep .01;echo $((${EPOCHREALTIME/.}-s))
44585
First time run, /bin/sleep was loaded from filesystem. this is longer 60676 - 10000 = 50676 -> 50 milliseconds!!
But anyway, even in cache memory, running a fork to sleep is ressource killer...
6.2. Using bash'sleep loadable module:
enable -f /usr/lib/bash/sleep sleep
s=${EPOCHREALTIME/.};sleep .01;echo $((${EPOCHREALTIME/.}-s))
10803
s=${EPOCHREALTIME/.};sleep .01;echo $((${EPOCHREALTIME/.}-s))
10760
Seem maybe a little better than using read -u $_dummySleepFd -t ... and still more readable in bash scripts.