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 interval
  • X 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)