How To Repeat A Command Every X Seconds On Linux
This article explains how to repeat a command every X seconds on Linux, in 2 ways: using watch
, and using sleep
in a while
loop (with a way to avoid time drift when using long-running commands).
Using watch
to run a command every X seconds
watch
can run a command repeatedly, displaying its output and errors (the first screenful). This allows you to watch the program output change over time.
Using watch
to run a command every X
seconds:
watch -n <X> <command>
Where:
-n
specifies the time intervalX
is the interval at which to run the command, in seconds (with 0.1 seconds being the lowest possible value)command
is the command you want to run
If the command contains pipes, quotes or other special characters, use single quotes, e.g.: 'command'
.
Example. The following command runs ps aux | grep firefox
every 5 seconds:
watch -n 5 'ps aux | grep firefox'
The watch
command has a few options, like highlighting the differences between successive updates (--differences
/ -d
), beep if a command has a non-zero exit (--beep
/ -b
), and more. Check out its man page for more information.
You might also like: How To Find Files Modified In The Last N Days Or Minutes Using find
Using sleep
in a while
loop to repeat a command every X seconds
Another way of repeating a command every X seconds on Linux is to use a while
loop with the sleep
command:
while true; do <command>; sleep <X>; done
Here, you need to replace command
with the command to run, and X
with the time interval in seconds (the number doesn't have to be an integer, but it can't be negative; you can also specify minutes - 1m for example, hours - 1h for example, etc.).
Example. The following one-liner runs the echo $(date)
command every 5 seconds:
while true; do echo $(date); sleep 5; done
If you're doing something more complicated, make sure that the interval is shorter than the time required for the command to finish, or else this might interfere with something in your script.
It's also important to note that using this, time will drift, depending on how long the command you're running takes to complete. For example, if you want to run a command every 5 seconds, but that command takes 3 seconds to complete, this will cause the command to run every 8 seconds, instead of every 5 seconds.
A solution for avoiding this time drift, and running the command every X seconds regardless of how long it takes to complete (as long as the command doesn't take more to complete than the sleep time), is to use the following:
while true; do <command>; sleep $((<X> - $(date +%s) % <X>)); done
Here, replace command
with the command you want to run, and both instances of X
with the number of seconds after which the command should be repeated.
[[Edit]] This solution is not perfect. Read the explanation and alternative to this in the marked edits further down this article.
Example. The following one-liner repeats the sleep 3
command (I couldn't think of a better example right now of a command that takes a few seconds to complete and isn't destructive in any way in case the user doesn't stop it - I'm typing this at 5 AM) every 5 seconds:
while true; do sleep 3; sleep $((5 - $(date +%s) % 5)); done
Using this, even though the command (sleep 3
in this case, it can be any command you want) takes 3 seconds to complete, the interval between runs is 5 seconds.
For testing this you can add an echo $(date)
at the beginning, like this:
while true; do echo $(date); sleep 3; sleep $((5 - $(date +%s) % 5)); done
You'll notice that the date printed in the terminal is in increments of 5 seconds, like this:
Thu 10 Dec 05:48:00 2020
Thu 10 Dec 05:48:05 2020
Thu 10 Dec 05:48:10 2020
Thu 10 Dec 05:48:15 2020
[[Edit]] This solution is not perfect though. Between the first and second run, the time interval may be shorter than the one you've specified. Subsequent runs use the correct time interval though. If you know a better way to do this, please leave a comment below.
[[Edit]] A solution (via stackexchange) which avoids time drifting, and doesn't have the issue I mentioned above (so no different time interval between the first and second run), but which doesn't make use of the sleep
command, is the following one-liner:
while true; do currentTime=$(date +%s); elapsedTime=$((currentTime - lastTime)); if [ $elapsedTime -ge <X> ]; then <command>; lastTime=$currentTime; i=0; fi; done
Replace X
with the time interval between runs (in seconds), and command
with the command you want to repeat every X seconds.
A bit of an explanation is in order here. This while
loop stores the current Unix time as currentTime
, then it subtracts the last time (lastTime
) from the current time, storing it as elapsedTime
. Next it checks if the elapsed time is greater or equal to X
and if it is, it runs command
(so the command is repeated every X
seconds). And finally, it sets the last time to be equal to the current time, so the next time it runs it can subtract lastTime
from the currentTime
, which gives the elapsedTime
value. I hope this makes sense to you as it does in my head 😁️.
Example. The following one-liner repeats the echo $(date)
and sleep 2
commands (I used sleep 2
so the command takes a couple of seconds to complete, to be able to check if time is drifting or not) every 5 seconds:
while true; do currentTime=$(date +%s); elapsedTime=$((currentTime - lastTime)); if [ $elapsedTime -ge 5 ]; then echo $(date); sleep 2; lastTime=$currentTime; i=0; fi; done
You might also like: How To Delete Files Older Or Newer Than N Days Using find (With Extra Examples)