For a bash script of mine I had to execute a certain command every second. While this command lasted less than a second, its duration was not always the same. Sometimes it would be done in 0.1 seconds, sometime in 0.5, and rarely in more than 1 second (that’s curl download for you). This variance made using a simple sleep
command a bit suboptimal.
What I needed was a command that would wait until the next full second. What I needed up with was this
SLEEP_SEC=`printf "0.%03d" $((1000 - 10#$(date +%N | head -c 3)))`
if [[ "$SLEEP_SEC" == "0.000" ]]; then SLEEP_SEC="1.000"; fi
sleep $SLEEP_SEC
The first line is just taking the current nano-second count and trimming it to the first three digits that are then subtracted from 1000
and prefixed with 0.
. This essentially gives millisecond precision count until the next full second. For example, if current nanosecond counter is 389123544
, this would result in 0.611
. And yes, you lose a bit of precision here as number gets truncated but the result will be precise enough.
If you wonder what is 10#
doing here, it’s just ensuring numbers starting with 0 are not misdetected by bash as octal numbers.
The if
conditional that follows is just to ensure there is at least 1 second of sleep if the previous command took less than 1 ms. Rare occurrence but cheap enough to protect against.
Finally, we send this to the sleep
command which will do its magic and allow the script to continue as the next second starts. And yes, it’s not a millisecond precise despite all this calculation as sleep
is not meant to be precise to start with. However, it is consistent and it triggers within the same 2-3 milliseconds almost every time. And that was plenty precise for me.