I often use this in various projects. I find it very useful. Thought I'd share to see if others find it useful.

Running something forever

Suppose you have some command that you want to run a lot. One way is to do this:

$ ./manage.py run-some-command && \
  ./manage.py run-some-command && \
  ./manage.py run-some-command && \
  ./manage.py run-some-command && \
  ./manage.py run-some-command && \
  ./manage.py run-some-command && \
  ./manage.py run-some-command && \
  ./manage.py run-some-command && \
  ./manage.py run-some-command && \
  ./manage.py run-some-command

That runs the command 10 times. Clunky but effective.

Another alternative is to hijack the watch command. By default it waits 2 seconds between each run but if the command takes longer than 2 seconds, it'll just wait. Running...

$ watch ./manage.py run-some-command

Is almost the same as running...:

$ clear && sleep 2 && ./manage.py run-some-command && \
  clear && sleep 2 && ./manage.py run-some-command && \
  clear && sleep 2 && ./manage.py run-some-command && \
  clear && sleep 2 && ./manage.py run-some-command && \
  clear && sleep 2 && ./manage.py run-some-command && \
  clear && sleep 2 && ./manage.py run-some-command && \
  ...
  ...forever until you Ctrl-C it...

But that's clunky too because you might not want it to clear the screen between each run and you get an un-necessary delay between each run.

The biggest problem is that with using watch or copy-n-paste the command many times with && between is that if you need to stop it you have to Ctrl-C and that might kill the command at a precious time.

A better solution

The important thing is that if you want to stop the command repeater, is that it gets to finish what it's working on at the moment.

Here's a great and simple solution:


#!/usr/bin/env bash
set -eo pipefail

_stopnow() {
  test -f stopnow && echo "Stopping!" && rm stopnow && exit 0 || return 0
}

while true
do
    _stopnow
    # Below here, you put in your command you want to run:

    ./manage.py run-some-command
done

Save that file as run-forever.sh and now you can do this:

$ bash run-forever.sh

It'll sit there and do its thing over and over. If you want to stop it (from another terminal):

$ touch stopnow

(the file stopnow will be deleted after it's spotted once)

Getting fancy

Instead of taking this bash script and editing it every time you need it to run a different command you can make it a globally available command. Here's how I do it:


#!/usr/bin/env bash
set -eo pipefail


count=0

_stopnow() {
    count="$(($count+1))"
    test -f stopnow && \
      echo "Stopping after $count iterations!" && \
      rm stopnow && exit 0 || return 0
}

control_c()
# run if user hits control-c
{
  echo "Managed to do $count iterations"
  exit $?
}

# trap keyboard interrupt (control-c)
trap control_c SIGINT

echo "To stop this forever loop created a file called stopnow."
echo "E.g: touch stopnow"
echo ""
echo "Now going to run '$@' forever"
echo ""
while true
do
    _stopnow

    eval $@

    # Do this in case you accidentally pass an argument
    # that finishes too quickly.
    sleep 1

done

This code in a Gist here.

Put this file in ~/bin/run-forever.sh and chmod +x ~/bin/run-forever.sh.

Now you can do this:

$ run-forever.sh ./manage.py run-some-command

If the command you want to run, forever, requires an operator you have to wrap everything in single quotation marks. For example:

$ run-forever.sh './manage.py run-some-command && echo "Cooling CPUs..." && sleep 10'

Comments

Post your own comment
Walter Drexel

How about just making a script file, 'foo.sh' and put in it some function to perform 'echo foo foo', then next line ./foo.sh to rerun the same script. It would run forever, no? Or until break is pressed.
Like so:

###
echo foo foo
./foo.sh
###

Peter Bengtsson

But, wouldn't that require you to create a script specifically for "foo" (e.g. foo.sh) and then when you have to do "bar" you have to create another script called bar.sh.

Levi Uzodike

What's the advantage of the control_c() function over just letting control C do what it does regularly? Won't it still have the possibility that it "might kill the command at a precious time"?

Peter Bengtsson

Isn't it to be able to do that last on-exit message about how many iterations it managed to do?

brett

thanks for this! just what i was looking for; handy script

NV

Doesn't work for me. I can CTRL + C and it doesn't re-run the .py script if it throws an error.

Your email will never ever be published.

Previous:
Be very careful with your add_header in Nginx! You might make your site insecure February 11, 2018 Linux, Web development, Nginx
Next:
Items function in JavaScript for looping over dictionaries like Python February 23, 2018 React, JavaScript
Related by category:
How to find which git SHA it was when you merged in the default branch February 26, 2026 Linux
hylite as an executable October 15, 2025 Linux
Elasticsearch memory usage December 11, 2025 Linux
How to count the number of non-blank lines with Bash September 3, 2025 Linux
Related by keyword:
gg shell completion August 13, 2025 Linux, JavaScript, Bun, macOS
Find the source of an alias in bash September 29, 2025 Linux, Bash, macOS
set -ex - The most useful bash trick of the year August 31, 2014 Linux
How to intercept and react to non-zero exits in bash February 23, 2023 Bash, GitHub