Running a loop precisely once per second

The name of the pictureThe name of the pictureThe name of the pictureClash Royale CLAN TAG#URR8PPP











up vote
31
down vote

favorite
5












I'm running this loop to check and print some things every second. However, because the calculations take maybe a few hundred milliseconds, the printed time sometimes skip a second.



Is there any way to write such a loop that I am guaranteed to get a printout every second? (Provided, of course, that the calculations in the loop take less than a second :))



while true; do
TIME=$(date +%H:%M:%S)
# some calculations which take a few hundred milliseconds
FOO=...
BAR=...
printf '%s %s %sn' $TIME $FOO $BAR
sleep 1
done






share|improve this question





















  • Possibly helpful: unix.stackexchange.com/q/60767/117549
    – Jeff Schaller
    Aug 6 at 15:18






  • 24




    Note that "precisely once per second" is not literally possible in most cases because you are (usually) running in userspace atop a preemptively multitasking kernel which will schedule your code as it sees fit (so that you might not regain control immediately after a sleep ends, for example). Unless you are writing C code which calls into the sched(7) API (POSIX: see <sched.h> and pages linked from there), you basically cannot have real-time guarantees of this form.
    – Kevin
    Aug 6 at 23:57











  • Just to back up what @Kevin said, using sleep() to try and get any sort of precise timing is doomed to failure, it only guarantees AT LEAST 1sec of sleep. If you really need precise timings you need to look at the system clock (see CLOCK_MONOTONIC) and trigger things based on time-since-last-event + 1s, and make sure you don't trip yourself up by taking >1sec to run, calculating the next time after some operation, etc.
    – John U
    Aug 7 at 12:07










  • just going to leave this here falsehoodsabouttime.com
    – alo Malbarez
    Aug 7 at 15:36










  • Precisely once a second = use a VCXO. A software-only solution will only get you to "good enough", but not precise.
    – Ian MacDonald
    2 days ago














up vote
31
down vote

favorite
5












I'm running this loop to check and print some things every second. However, because the calculations take maybe a few hundred milliseconds, the printed time sometimes skip a second.



Is there any way to write such a loop that I am guaranteed to get a printout every second? (Provided, of course, that the calculations in the loop take less than a second :))



while true; do
TIME=$(date +%H:%M:%S)
# some calculations which take a few hundred milliseconds
FOO=...
BAR=...
printf '%s %s %sn' $TIME $FOO $BAR
sleep 1
done






share|improve this question





















  • Possibly helpful: unix.stackexchange.com/q/60767/117549
    – Jeff Schaller
    Aug 6 at 15:18






  • 24




    Note that "precisely once per second" is not literally possible in most cases because you are (usually) running in userspace atop a preemptively multitasking kernel which will schedule your code as it sees fit (so that you might not regain control immediately after a sleep ends, for example). Unless you are writing C code which calls into the sched(7) API (POSIX: see <sched.h> and pages linked from there), you basically cannot have real-time guarantees of this form.
    – Kevin
    Aug 6 at 23:57











  • Just to back up what @Kevin said, using sleep() to try and get any sort of precise timing is doomed to failure, it only guarantees AT LEAST 1sec of sleep. If you really need precise timings you need to look at the system clock (see CLOCK_MONOTONIC) and trigger things based on time-since-last-event + 1s, and make sure you don't trip yourself up by taking >1sec to run, calculating the next time after some operation, etc.
    – John U
    Aug 7 at 12:07










  • just going to leave this here falsehoodsabouttime.com
    – alo Malbarez
    Aug 7 at 15:36










  • Precisely once a second = use a VCXO. A software-only solution will only get you to "good enough", but not precise.
    – Ian MacDonald
    2 days ago












up vote
31
down vote

favorite
5









up vote
31
down vote

favorite
5






5





I'm running this loop to check and print some things every second. However, because the calculations take maybe a few hundred milliseconds, the printed time sometimes skip a second.



Is there any way to write such a loop that I am guaranteed to get a printout every second? (Provided, of course, that the calculations in the loop take less than a second :))



while true; do
TIME=$(date +%H:%M:%S)
# some calculations which take a few hundred milliseconds
FOO=...
BAR=...
printf '%s %s %sn' $TIME $FOO $BAR
sleep 1
done






share|improve this question













I'm running this loop to check and print some things every second. However, because the calculations take maybe a few hundred milliseconds, the printed time sometimes skip a second.



Is there any way to write such a loop that I am guaranteed to get a printout every second? (Provided, of course, that the calculations in the loop take less than a second :))



while true; do
TIME=$(date +%H:%M:%S)
# some calculations which take a few hundred milliseconds
FOO=...
BAR=...
printf '%s %s %sn' $TIME $FOO $BAR
sleep 1
done








share|improve this question












share|improve this question




share|improve this question








edited Aug 7 at 11:04









Jeff Schaller

30.8k846104




30.8k846104









asked Aug 6 at 14:57









forthrin

785721




785721











  • Possibly helpful: unix.stackexchange.com/q/60767/117549
    – Jeff Schaller
    Aug 6 at 15:18






  • 24




    Note that "precisely once per second" is not literally possible in most cases because you are (usually) running in userspace atop a preemptively multitasking kernel which will schedule your code as it sees fit (so that you might not regain control immediately after a sleep ends, for example). Unless you are writing C code which calls into the sched(7) API (POSIX: see <sched.h> and pages linked from there), you basically cannot have real-time guarantees of this form.
    – Kevin
    Aug 6 at 23:57











  • Just to back up what @Kevin said, using sleep() to try and get any sort of precise timing is doomed to failure, it only guarantees AT LEAST 1sec of sleep. If you really need precise timings you need to look at the system clock (see CLOCK_MONOTONIC) and trigger things based on time-since-last-event + 1s, and make sure you don't trip yourself up by taking >1sec to run, calculating the next time after some operation, etc.
    – John U
    Aug 7 at 12:07










  • just going to leave this here falsehoodsabouttime.com
    – alo Malbarez
    Aug 7 at 15:36










  • Precisely once a second = use a VCXO. A software-only solution will only get you to "good enough", but not precise.
    – Ian MacDonald
    2 days ago
















  • Possibly helpful: unix.stackexchange.com/q/60767/117549
    – Jeff Schaller
    Aug 6 at 15:18






  • 24




    Note that "precisely once per second" is not literally possible in most cases because you are (usually) running in userspace atop a preemptively multitasking kernel which will schedule your code as it sees fit (so that you might not regain control immediately after a sleep ends, for example). Unless you are writing C code which calls into the sched(7) API (POSIX: see <sched.h> and pages linked from there), you basically cannot have real-time guarantees of this form.
    – Kevin
    Aug 6 at 23:57











  • Just to back up what @Kevin said, using sleep() to try and get any sort of precise timing is doomed to failure, it only guarantees AT LEAST 1sec of sleep. If you really need precise timings you need to look at the system clock (see CLOCK_MONOTONIC) and trigger things based on time-since-last-event + 1s, and make sure you don't trip yourself up by taking >1sec to run, calculating the next time after some operation, etc.
    – John U
    Aug 7 at 12:07










  • just going to leave this here falsehoodsabouttime.com
    – alo Malbarez
    Aug 7 at 15:36










  • Precisely once a second = use a VCXO. A software-only solution will only get you to "good enough", but not precise.
    – Ian MacDonald
    2 days ago















Possibly helpful: unix.stackexchange.com/q/60767/117549
– Jeff Schaller
Aug 6 at 15:18




Possibly helpful: unix.stackexchange.com/q/60767/117549
– Jeff Schaller
Aug 6 at 15:18




24




24




Note that "precisely once per second" is not literally possible in most cases because you are (usually) running in userspace atop a preemptively multitasking kernel which will schedule your code as it sees fit (so that you might not regain control immediately after a sleep ends, for example). Unless you are writing C code which calls into the sched(7) API (POSIX: see <sched.h> and pages linked from there), you basically cannot have real-time guarantees of this form.
– Kevin
Aug 6 at 23:57





Note that "precisely once per second" is not literally possible in most cases because you are (usually) running in userspace atop a preemptively multitasking kernel which will schedule your code as it sees fit (so that you might not regain control immediately after a sleep ends, for example). Unless you are writing C code which calls into the sched(7) API (POSIX: see <sched.h> and pages linked from there), you basically cannot have real-time guarantees of this form.
– Kevin
Aug 6 at 23:57













Just to back up what @Kevin said, using sleep() to try and get any sort of precise timing is doomed to failure, it only guarantees AT LEAST 1sec of sleep. If you really need precise timings you need to look at the system clock (see CLOCK_MONOTONIC) and trigger things based on time-since-last-event + 1s, and make sure you don't trip yourself up by taking >1sec to run, calculating the next time after some operation, etc.
– John U
Aug 7 at 12:07




Just to back up what @Kevin said, using sleep() to try and get any sort of precise timing is doomed to failure, it only guarantees AT LEAST 1sec of sleep. If you really need precise timings you need to look at the system clock (see CLOCK_MONOTONIC) and trigger things based on time-since-last-event + 1s, and make sure you don't trip yourself up by taking >1sec to run, calculating the next time after some operation, etc.
– John U
Aug 7 at 12:07












just going to leave this here falsehoodsabouttime.com
– alo Malbarez
Aug 7 at 15:36




just going to leave this here falsehoodsabouttime.com
– alo Malbarez
Aug 7 at 15:36












Precisely once a second = use a VCXO. A software-only solution will only get you to "good enough", but not precise.
– Ian MacDonald
2 days ago




Precisely once a second = use a VCXO. A software-only solution will only get you to "good enough", but not precise.
– Ian MacDonald
2 days ago










5 Answers
5






active

oldest

votes

















up vote
60
down vote



accepted










To stay a bit closer to the original code, what I do is:



while true; do
sleep 1 &
...your stuff here...
wait # for sleep
done


This changes the semantics a little: if your stuff took less than a second, it will simply wait for the full second to pass. However, if your stuff takes longer than a second for any reason, it won't keep spawning even more subprocesses with never any end to it.



So your stuff never runs in parallel, and not in the background, so variables work as expected too.



Note that if you do start additional background tasks as well, you'll have to change the wait instruction to only wait for the sleep process specifically.



If you need it to be even more accurate, you'll probably just have to sync it to the system clock and sleep ms instead of full seconds.




How to sync to system clock? No idea really, stupid attempt:



Default:



while sleep 1
do
date +%N
done


Output: 003511461 010510925 016081282 021643477 028504349 03... (keeps growing)



Synced:



 while sleep 0.$((1999999999 - 1$(date +%N)))
do
date +%N
done


Output: 002648691 001098397 002514348 001293023 001679137 00... (stays same)






share|improve this answer



















  • 6




    This sleep/wait trick is really clever !
    – philfr
    2 days ago










  • I'm wondering if all implementations of sleep handle fractional seconds?
    – jcaron
    yesterday






  • 1




    @jcaron not all of them. but it works for gnu sleep and busybox sleep so it's not exotic. You could probably do a simple fallback like sleep 0.9 || sleep 1 as invalid parameter is pretty much the only reason for sleep to ever fail.
    – frostschutz
    yesterday











  • @frostschutz I'd expect sleep 0.9 to be interpreted as sleep 0 by naïve implementations (given that's what atoi would do). Not sure if that would actually result in an error.
    – jcaron
    yesterday






  • 1




    I'm happy to see this question sparked a lot of interest. Your suggestion and answer are very good. Not only does it keep within the second, it also sticks as close to the whole second as possible. Impressive! (PS! On a side note, one must install GNU Coreutils and use gdate on macOS to make date +%N work.)
    – forthrin
    yesterday


















up vote
29
down vote













If you can restructure your loop into a script / oneliner then the simplest way to do this is with watch and its precise option.



You can see the effect with watch -n 1 sleep 0.5 - it will show seconds counting up, but will occasionally skip over a second. Running it as watch -n 1 -p sleep 0.5 will output twice per second, every second, and you won't see any skips.






share|improve this answer




























    up vote
    10
    down vote













    Running the operations in a subshell that runs as a background job would make them not interfere so much with the sleep.



    while true; do
    (
    TIME=$(date +%T)
    # some calculations which take a few hundred milliseconds
    FOO=...
    BAR=...
    printf '%s %s %sn' "$TIME" "$FOO" "$BAR"
    ) &
    sleep 1
    done


    The only time "stolen" from the one second would be the time taken to launch the subshell, so it would eventually skip a second, but hopefully less often than the original code.



    If the code in the subshell ends up using more than a second, the loop would start to accumulate background jobs and eventually run out of resources.






    share|improve this answer






























      up vote
      7
      down vote













      With zsh:



      n=0
      typeset -F SECONDS=0
      while true; do
      date '+%FT%T.%2N%z'
      ((++n > SECONDS)) && sleep $((n - SECONDS))
      done


      If your sleep doesn't support floating point seconds, you can use zsh's zselect instead (after a zmodload zsh/zselect):



      zmodload zsh/zselect
      n=0
      typeset -F SECONDS=0
      while true; do
      date '+%FZ%T.%2N%z'
      ((++n > SECONDS)) && zselect -t $((((n - SECONDS) * 100) | 0))
      done


      Those should not drift as long as the commands in the loop take less than one second to run.






      share|improve this answer






























        up vote
        7
        down vote













        Another alternative (if you can't use, e.g., watch -p as Maelstrom suggests) is sleepenh [manpage], which is designed for this.



        Example:



        #!/bin/sh

        t=$(sleepenh 0)
        while true; do
        date +'sec=%s ns=%N'
        sleep 0.2
        t=$(sleepenh $t 1)
        done


        Note the sleep 0.2 in there the simulate doing some time-consuming task eating around 200ms. Despite that, the nanoseconds output remain stable (well, by non-realtime OS standards) — it happens once per second:



        sec=1533663406 ns=840039402
        sec=1533663407 ns=840105387
        sec=1533663408 ns=840380678
        sec=1533663409 ns=840175397
        sec=1533663410 ns=840132883
        sec=1533663411 ns=840263150
        sec=1533663412 ns=840246082
        sec=1533663413 ns=840259567
        sec=1533663414 ns=840066687


        That's under 1ms different, and no trend. That's quite good; you should expect bounces of at least 10ms if there is any load on the system — but still no drift over time. I.e., you won't lose a second.






        share|improve this answer





















          Your Answer







          StackExchange.ready(function()
          var channelOptions =
          tags: "".split(" "),
          id: "106"
          ;
          initTagRenderer("".split(" "), "".split(" "), channelOptions);

          StackExchange.using("externalEditor", function()
          // Have to fire editor after snippets, if snippets enabled
          if (StackExchange.settings.snippets.snippetsEnabled)
          StackExchange.using("snippets", function()
          createEditor();
          );

          else
          createEditor();

          );

          function createEditor()
          StackExchange.prepareEditor(
          heartbeatType: 'answer',
          convertImagesToLinks: false,
          noModals: false,
          showLowRepImageUploadWarning: true,
          reputationToPostImages: null,
          bindNavPrevention: true,
          postfix: "",
          onDemand: true,
          discardSelector: ".discard-answer"
          ,immediatelyShowMarkdownHelp:true
          );



          );








           

          draft saved


          draft discarded


















          StackExchange.ready(
          function ()
          StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2funix.stackexchange.com%2fquestions%2f460836%2frunning-a-loop-precisely-once-per-second%23new-answer', 'question_page');

          );

          Post as a guest






























          5 Answers
          5






          active

          oldest

          votes








          5 Answers
          5






          active

          oldest

          votes









          active

          oldest

          votes






          active

          oldest

          votes








          up vote
          60
          down vote



          accepted










          To stay a bit closer to the original code, what I do is:



          while true; do
          sleep 1 &
          ...your stuff here...
          wait # for sleep
          done


          This changes the semantics a little: if your stuff took less than a second, it will simply wait for the full second to pass. However, if your stuff takes longer than a second for any reason, it won't keep spawning even more subprocesses with never any end to it.



          So your stuff never runs in parallel, and not in the background, so variables work as expected too.



          Note that if you do start additional background tasks as well, you'll have to change the wait instruction to only wait for the sleep process specifically.



          If you need it to be even more accurate, you'll probably just have to sync it to the system clock and sleep ms instead of full seconds.




          How to sync to system clock? No idea really, stupid attempt:



          Default:



          while sleep 1
          do
          date +%N
          done


          Output: 003511461 010510925 016081282 021643477 028504349 03... (keeps growing)



          Synced:



           while sleep 0.$((1999999999 - 1$(date +%N)))
          do
          date +%N
          done


          Output: 002648691 001098397 002514348 001293023 001679137 00... (stays same)






          share|improve this answer



















          • 6




            This sleep/wait trick is really clever !
            – philfr
            2 days ago










          • I'm wondering if all implementations of sleep handle fractional seconds?
            – jcaron
            yesterday






          • 1




            @jcaron not all of them. but it works for gnu sleep and busybox sleep so it's not exotic. You could probably do a simple fallback like sleep 0.9 || sleep 1 as invalid parameter is pretty much the only reason for sleep to ever fail.
            – frostschutz
            yesterday











          • @frostschutz I'd expect sleep 0.9 to be interpreted as sleep 0 by naïve implementations (given that's what atoi would do). Not sure if that would actually result in an error.
            – jcaron
            yesterday






          • 1




            I'm happy to see this question sparked a lot of interest. Your suggestion and answer are very good. Not only does it keep within the second, it also sticks as close to the whole second as possible. Impressive! (PS! On a side note, one must install GNU Coreutils and use gdate on macOS to make date +%N work.)
            – forthrin
            yesterday















          up vote
          60
          down vote



          accepted










          To stay a bit closer to the original code, what I do is:



          while true; do
          sleep 1 &
          ...your stuff here...
          wait # for sleep
          done


          This changes the semantics a little: if your stuff took less than a second, it will simply wait for the full second to pass. However, if your stuff takes longer than a second for any reason, it won't keep spawning even more subprocesses with never any end to it.



          So your stuff never runs in parallel, and not in the background, so variables work as expected too.



          Note that if you do start additional background tasks as well, you'll have to change the wait instruction to only wait for the sleep process specifically.



          If you need it to be even more accurate, you'll probably just have to sync it to the system clock and sleep ms instead of full seconds.




          How to sync to system clock? No idea really, stupid attempt:



          Default:



          while sleep 1
          do
          date +%N
          done


          Output: 003511461 010510925 016081282 021643477 028504349 03... (keeps growing)



          Synced:



           while sleep 0.$((1999999999 - 1$(date +%N)))
          do
          date +%N
          done


          Output: 002648691 001098397 002514348 001293023 001679137 00... (stays same)






          share|improve this answer



















          • 6




            This sleep/wait trick is really clever !
            – philfr
            2 days ago










          • I'm wondering if all implementations of sleep handle fractional seconds?
            – jcaron
            yesterday






          • 1




            @jcaron not all of them. but it works for gnu sleep and busybox sleep so it's not exotic. You could probably do a simple fallback like sleep 0.9 || sleep 1 as invalid parameter is pretty much the only reason for sleep to ever fail.
            – frostschutz
            yesterday











          • @frostschutz I'd expect sleep 0.9 to be interpreted as sleep 0 by naïve implementations (given that's what atoi would do). Not sure if that would actually result in an error.
            – jcaron
            yesterday






          • 1




            I'm happy to see this question sparked a lot of interest. Your suggestion and answer are very good. Not only does it keep within the second, it also sticks as close to the whole second as possible. Impressive! (PS! On a side note, one must install GNU Coreutils and use gdate on macOS to make date +%N work.)
            – forthrin
            yesterday













          up vote
          60
          down vote



          accepted







          up vote
          60
          down vote



          accepted






          To stay a bit closer to the original code, what I do is:



          while true; do
          sleep 1 &
          ...your stuff here...
          wait # for sleep
          done


          This changes the semantics a little: if your stuff took less than a second, it will simply wait for the full second to pass. However, if your stuff takes longer than a second for any reason, it won't keep spawning even more subprocesses with never any end to it.



          So your stuff never runs in parallel, and not in the background, so variables work as expected too.



          Note that if you do start additional background tasks as well, you'll have to change the wait instruction to only wait for the sleep process specifically.



          If you need it to be even more accurate, you'll probably just have to sync it to the system clock and sleep ms instead of full seconds.




          How to sync to system clock? No idea really, stupid attempt:



          Default:



          while sleep 1
          do
          date +%N
          done


          Output: 003511461 010510925 016081282 021643477 028504349 03... (keeps growing)



          Synced:



           while sleep 0.$((1999999999 - 1$(date +%N)))
          do
          date +%N
          done


          Output: 002648691 001098397 002514348 001293023 001679137 00... (stays same)






          share|improve this answer















          To stay a bit closer to the original code, what I do is:



          while true; do
          sleep 1 &
          ...your stuff here...
          wait # for sleep
          done


          This changes the semantics a little: if your stuff took less than a second, it will simply wait for the full second to pass. However, if your stuff takes longer than a second for any reason, it won't keep spawning even more subprocesses with never any end to it.



          So your stuff never runs in parallel, and not in the background, so variables work as expected too.



          Note that if you do start additional background tasks as well, you'll have to change the wait instruction to only wait for the sleep process specifically.



          If you need it to be even more accurate, you'll probably just have to sync it to the system clock and sleep ms instead of full seconds.




          How to sync to system clock? No idea really, stupid attempt:



          Default:



          while sleep 1
          do
          date +%N
          done


          Output: 003511461 010510925 016081282 021643477 028504349 03... (keeps growing)



          Synced:



           while sleep 0.$((1999999999 - 1$(date +%N)))
          do
          date +%N
          done


          Output: 002648691 001098397 002514348 001293023 001679137 00... (stays same)







          share|improve this answer















          share|improve this answer



          share|improve this answer








          edited Aug 6 at 15:34


























          answered Aug 6 at 15:20









          frostschutz

          24.1k14570




          24.1k14570







          • 6




            This sleep/wait trick is really clever !
            – philfr
            2 days ago










          • I'm wondering if all implementations of sleep handle fractional seconds?
            – jcaron
            yesterday






          • 1




            @jcaron not all of them. but it works for gnu sleep and busybox sleep so it's not exotic. You could probably do a simple fallback like sleep 0.9 || sleep 1 as invalid parameter is pretty much the only reason for sleep to ever fail.
            – frostschutz
            yesterday











          • @frostschutz I'd expect sleep 0.9 to be interpreted as sleep 0 by naïve implementations (given that's what atoi would do). Not sure if that would actually result in an error.
            – jcaron
            yesterday






          • 1




            I'm happy to see this question sparked a lot of interest. Your suggestion and answer are very good. Not only does it keep within the second, it also sticks as close to the whole second as possible. Impressive! (PS! On a side note, one must install GNU Coreutils and use gdate on macOS to make date +%N work.)
            – forthrin
            yesterday













          • 6




            This sleep/wait trick is really clever !
            – philfr
            2 days ago










          • I'm wondering if all implementations of sleep handle fractional seconds?
            – jcaron
            yesterday






          • 1




            @jcaron not all of them. but it works for gnu sleep and busybox sleep so it's not exotic. You could probably do a simple fallback like sleep 0.9 || sleep 1 as invalid parameter is pretty much the only reason for sleep to ever fail.
            – frostschutz
            yesterday











          • @frostschutz I'd expect sleep 0.9 to be interpreted as sleep 0 by naïve implementations (given that's what atoi would do). Not sure if that would actually result in an error.
            – jcaron
            yesterday






          • 1




            I'm happy to see this question sparked a lot of interest. Your suggestion and answer are very good. Not only does it keep within the second, it also sticks as close to the whole second as possible. Impressive! (PS! On a side note, one must install GNU Coreutils and use gdate on macOS to make date +%N work.)
            – forthrin
            yesterday








          6




          6




          This sleep/wait trick is really clever !
          – philfr
          2 days ago




          This sleep/wait trick is really clever !
          – philfr
          2 days ago












          I'm wondering if all implementations of sleep handle fractional seconds?
          – jcaron
          yesterday




          I'm wondering if all implementations of sleep handle fractional seconds?
          – jcaron
          yesterday




          1




          1




          @jcaron not all of them. but it works for gnu sleep and busybox sleep so it's not exotic. You could probably do a simple fallback like sleep 0.9 || sleep 1 as invalid parameter is pretty much the only reason for sleep to ever fail.
          – frostschutz
          yesterday





          @jcaron not all of them. but it works for gnu sleep and busybox sleep so it's not exotic. You could probably do a simple fallback like sleep 0.9 || sleep 1 as invalid parameter is pretty much the only reason for sleep to ever fail.
          – frostschutz
          yesterday













          @frostschutz I'd expect sleep 0.9 to be interpreted as sleep 0 by naïve implementations (given that's what atoi would do). Not sure if that would actually result in an error.
          – jcaron
          yesterday




          @frostschutz I'd expect sleep 0.9 to be interpreted as sleep 0 by naïve implementations (given that's what atoi would do). Not sure if that would actually result in an error.
          – jcaron
          yesterday




          1




          1




          I'm happy to see this question sparked a lot of interest. Your suggestion and answer are very good. Not only does it keep within the second, it also sticks as close to the whole second as possible. Impressive! (PS! On a side note, one must install GNU Coreutils and use gdate on macOS to make date +%N work.)
          – forthrin
          yesterday





          I'm happy to see this question sparked a lot of interest. Your suggestion and answer are very good. Not only does it keep within the second, it also sticks as close to the whole second as possible. Impressive! (PS! On a side note, one must install GNU Coreutils and use gdate on macOS to make date +%N work.)
          – forthrin
          yesterday













          up vote
          29
          down vote













          If you can restructure your loop into a script / oneliner then the simplest way to do this is with watch and its precise option.



          You can see the effect with watch -n 1 sleep 0.5 - it will show seconds counting up, but will occasionally skip over a second. Running it as watch -n 1 -p sleep 0.5 will output twice per second, every second, and you won't see any skips.






          share|improve this answer

























            up vote
            29
            down vote













            If you can restructure your loop into a script / oneliner then the simplest way to do this is with watch and its precise option.



            You can see the effect with watch -n 1 sleep 0.5 - it will show seconds counting up, but will occasionally skip over a second. Running it as watch -n 1 -p sleep 0.5 will output twice per second, every second, and you won't see any skips.






            share|improve this answer























              up vote
              29
              down vote










              up vote
              29
              down vote









              If you can restructure your loop into a script / oneliner then the simplest way to do this is with watch and its precise option.



              You can see the effect with watch -n 1 sleep 0.5 - it will show seconds counting up, but will occasionally skip over a second. Running it as watch -n 1 -p sleep 0.5 will output twice per second, every second, and you won't see any skips.






              share|improve this answer













              If you can restructure your loop into a script / oneliner then the simplest way to do this is with watch and its precise option.



              You can see the effect with watch -n 1 sleep 0.5 - it will show seconds counting up, but will occasionally skip over a second. Running it as watch -n 1 -p sleep 0.5 will output twice per second, every second, and you won't see any skips.







              share|improve this answer













              share|improve this answer



              share|improve this answer











              answered Aug 7 at 0:10









              Maelstrom

              39113




              39113




















                  up vote
                  10
                  down vote













                  Running the operations in a subshell that runs as a background job would make them not interfere so much with the sleep.



                  while true; do
                  (
                  TIME=$(date +%T)
                  # some calculations which take a few hundred milliseconds
                  FOO=...
                  BAR=...
                  printf '%s %s %sn' "$TIME" "$FOO" "$BAR"
                  ) &
                  sleep 1
                  done


                  The only time "stolen" from the one second would be the time taken to launch the subshell, so it would eventually skip a second, but hopefully less often than the original code.



                  If the code in the subshell ends up using more than a second, the loop would start to accumulate background jobs and eventually run out of resources.






                  share|improve this answer



























                    up vote
                    10
                    down vote













                    Running the operations in a subshell that runs as a background job would make them not interfere so much with the sleep.



                    while true; do
                    (
                    TIME=$(date +%T)
                    # some calculations which take a few hundred milliseconds
                    FOO=...
                    BAR=...
                    printf '%s %s %sn' "$TIME" "$FOO" "$BAR"
                    ) &
                    sleep 1
                    done


                    The only time "stolen" from the one second would be the time taken to launch the subshell, so it would eventually skip a second, but hopefully less often than the original code.



                    If the code in the subshell ends up using more than a second, the loop would start to accumulate background jobs and eventually run out of resources.






                    share|improve this answer

























                      up vote
                      10
                      down vote










                      up vote
                      10
                      down vote









                      Running the operations in a subshell that runs as a background job would make them not interfere so much with the sleep.



                      while true; do
                      (
                      TIME=$(date +%T)
                      # some calculations which take a few hundred milliseconds
                      FOO=...
                      BAR=...
                      printf '%s %s %sn' "$TIME" "$FOO" "$BAR"
                      ) &
                      sleep 1
                      done


                      The only time "stolen" from the one second would be the time taken to launch the subshell, so it would eventually skip a second, but hopefully less often than the original code.



                      If the code in the subshell ends up using more than a second, the loop would start to accumulate background jobs and eventually run out of resources.






                      share|improve this answer















                      Running the operations in a subshell that runs as a background job would make them not interfere so much with the sleep.



                      while true; do
                      (
                      TIME=$(date +%T)
                      # some calculations which take a few hundred milliseconds
                      FOO=...
                      BAR=...
                      printf '%s %s %sn' "$TIME" "$FOO" "$BAR"
                      ) &
                      sleep 1
                      done


                      The only time "stolen" from the one second would be the time taken to launch the subshell, so it would eventually skip a second, but hopefully less often than the original code.



                      If the code in the subshell ends up using more than a second, the loop would start to accumulate background jobs and eventually run out of resources.







                      share|improve this answer















                      share|improve this answer



                      share|improve this answer








                      edited Aug 6 at 15:11


























                      answered Aug 6 at 15:02









                      Kusalananda

                      101k13199312




                      101k13199312




















                          up vote
                          7
                          down vote













                          With zsh:



                          n=0
                          typeset -F SECONDS=0
                          while true; do
                          date '+%FT%T.%2N%z'
                          ((++n > SECONDS)) && sleep $((n - SECONDS))
                          done


                          If your sleep doesn't support floating point seconds, you can use zsh's zselect instead (after a zmodload zsh/zselect):



                          zmodload zsh/zselect
                          n=0
                          typeset -F SECONDS=0
                          while true; do
                          date '+%FZ%T.%2N%z'
                          ((++n > SECONDS)) && zselect -t $((((n - SECONDS) * 100) | 0))
                          done


                          Those should not drift as long as the commands in the loop take less than one second to run.






                          share|improve this answer



























                            up vote
                            7
                            down vote













                            With zsh:



                            n=0
                            typeset -F SECONDS=0
                            while true; do
                            date '+%FT%T.%2N%z'
                            ((++n > SECONDS)) && sleep $((n - SECONDS))
                            done


                            If your sleep doesn't support floating point seconds, you can use zsh's zselect instead (after a zmodload zsh/zselect):



                            zmodload zsh/zselect
                            n=0
                            typeset -F SECONDS=0
                            while true; do
                            date '+%FZ%T.%2N%z'
                            ((++n > SECONDS)) && zselect -t $((((n - SECONDS) * 100) | 0))
                            done


                            Those should not drift as long as the commands in the loop take less than one second to run.






                            share|improve this answer

























                              up vote
                              7
                              down vote










                              up vote
                              7
                              down vote









                              With zsh:



                              n=0
                              typeset -F SECONDS=0
                              while true; do
                              date '+%FT%T.%2N%z'
                              ((++n > SECONDS)) && sleep $((n - SECONDS))
                              done


                              If your sleep doesn't support floating point seconds, you can use zsh's zselect instead (after a zmodload zsh/zselect):



                              zmodload zsh/zselect
                              n=0
                              typeset -F SECONDS=0
                              while true; do
                              date '+%FZ%T.%2N%z'
                              ((++n > SECONDS)) && zselect -t $((((n - SECONDS) * 100) | 0))
                              done


                              Those should not drift as long as the commands in the loop take less than one second to run.






                              share|improve this answer















                              With zsh:



                              n=0
                              typeset -F SECONDS=0
                              while true; do
                              date '+%FT%T.%2N%z'
                              ((++n > SECONDS)) && sleep $((n - SECONDS))
                              done


                              If your sleep doesn't support floating point seconds, you can use zsh's zselect instead (after a zmodload zsh/zselect):



                              zmodload zsh/zselect
                              n=0
                              typeset -F SECONDS=0
                              while true; do
                              date '+%FZ%T.%2N%z'
                              ((++n > SECONDS)) && zselect -t $((((n - SECONDS) * 100) | 0))
                              done


                              Those should not drift as long as the commands in the loop take less than one second to run.







                              share|improve this answer















                              share|improve this answer



                              share|improve this answer








                              edited Aug 7 at 12:02


























                              answered Aug 7 at 6:42









                              Stéphane Chazelas

                              278k52513844




                              278k52513844




















                                  up vote
                                  7
                                  down vote













                                  Another alternative (if you can't use, e.g., watch -p as Maelstrom suggests) is sleepenh [manpage], which is designed for this.



                                  Example:



                                  #!/bin/sh

                                  t=$(sleepenh 0)
                                  while true; do
                                  date +'sec=%s ns=%N'
                                  sleep 0.2
                                  t=$(sleepenh $t 1)
                                  done


                                  Note the sleep 0.2 in there the simulate doing some time-consuming task eating around 200ms. Despite that, the nanoseconds output remain stable (well, by non-realtime OS standards) — it happens once per second:



                                  sec=1533663406 ns=840039402
                                  sec=1533663407 ns=840105387
                                  sec=1533663408 ns=840380678
                                  sec=1533663409 ns=840175397
                                  sec=1533663410 ns=840132883
                                  sec=1533663411 ns=840263150
                                  sec=1533663412 ns=840246082
                                  sec=1533663413 ns=840259567
                                  sec=1533663414 ns=840066687


                                  That's under 1ms different, and no trend. That's quite good; you should expect bounces of at least 10ms if there is any load on the system — but still no drift over time. I.e., you won't lose a second.






                                  share|improve this answer

























                                    up vote
                                    7
                                    down vote













                                    Another alternative (if you can't use, e.g., watch -p as Maelstrom suggests) is sleepenh [manpage], which is designed for this.



                                    Example:



                                    #!/bin/sh

                                    t=$(sleepenh 0)
                                    while true; do
                                    date +'sec=%s ns=%N'
                                    sleep 0.2
                                    t=$(sleepenh $t 1)
                                    done


                                    Note the sleep 0.2 in there the simulate doing some time-consuming task eating around 200ms. Despite that, the nanoseconds output remain stable (well, by non-realtime OS standards) — it happens once per second:



                                    sec=1533663406 ns=840039402
                                    sec=1533663407 ns=840105387
                                    sec=1533663408 ns=840380678
                                    sec=1533663409 ns=840175397
                                    sec=1533663410 ns=840132883
                                    sec=1533663411 ns=840263150
                                    sec=1533663412 ns=840246082
                                    sec=1533663413 ns=840259567
                                    sec=1533663414 ns=840066687


                                    That's under 1ms different, and no trend. That's quite good; you should expect bounces of at least 10ms if there is any load on the system — but still no drift over time. I.e., you won't lose a second.






                                    share|improve this answer























                                      up vote
                                      7
                                      down vote










                                      up vote
                                      7
                                      down vote









                                      Another alternative (if you can't use, e.g., watch -p as Maelstrom suggests) is sleepenh [manpage], which is designed for this.



                                      Example:



                                      #!/bin/sh

                                      t=$(sleepenh 0)
                                      while true; do
                                      date +'sec=%s ns=%N'
                                      sleep 0.2
                                      t=$(sleepenh $t 1)
                                      done


                                      Note the sleep 0.2 in there the simulate doing some time-consuming task eating around 200ms. Despite that, the nanoseconds output remain stable (well, by non-realtime OS standards) — it happens once per second:



                                      sec=1533663406 ns=840039402
                                      sec=1533663407 ns=840105387
                                      sec=1533663408 ns=840380678
                                      sec=1533663409 ns=840175397
                                      sec=1533663410 ns=840132883
                                      sec=1533663411 ns=840263150
                                      sec=1533663412 ns=840246082
                                      sec=1533663413 ns=840259567
                                      sec=1533663414 ns=840066687


                                      That's under 1ms different, and no trend. That's quite good; you should expect bounces of at least 10ms if there is any load on the system — but still no drift over time. I.e., you won't lose a second.






                                      share|improve this answer













                                      Another alternative (if you can't use, e.g., watch -p as Maelstrom suggests) is sleepenh [manpage], which is designed for this.



                                      Example:



                                      #!/bin/sh

                                      t=$(sleepenh 0)
                                      while true; do
                                      date +'sec=%s ns=%N'
                                      sleep 0.2
                                      t=$(sleepenh $t 1)
                                      done


                                      Note the sleep 0.2 in there the simulate doing some time-consuming task eating around 200ms. Despite that, the nanoseconds output remain stable (well, by non-realtime OS standards) — it happens once per second:



                                      sec=1533663406 ns=840039402
                                      sec=1533663407 ns=840105387
                                      sec=1533663408 ns=840380678
                                      sec=1533663409 ns=840175397
                                      sec=1533663410 ns=840132883
                                      sec=1533663411 ns=840263150
                                      sec=1533663412 ns=840246082
                                      sec=1533663413 ns=840259567
                                      sec=1533663414 ns=840066687


                                      That's under 1ms different, and no trend. That's quite good; you should expect bounces of at least 10ms if there is any load on the system — but still no drift over time. I.e., you won't lose a second.







                                      share|improve this answer













                                      share|improve this answer



                                      share|improve this answer











                                      answered Aug 7 at 17:41









                                      derobert

                                      68.1k8146202




                                      68.1k8146202






















                                           

                                          draft saved


                                          draft discarded


























                                           


                                          draft saved


                                          draft discarded














                                          StackExchange.ready(
                                          function ()
                                          StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2funix.stackexchange.com%2fquestions%2f460836%2frunning-a-loop-precisely-once-per-second%23new-answer', 'question_page');

                                          );

                                          Post as a guest













































































                                          Popular posts from this blog

                                          Greedy Best First Search implementation in Rust

                                          Function to Return a JSON Like Objects Using VBA Collections and Arrays

                                          C++11 CLH Lock Implementation