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
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 commentHow 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
###
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.
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"?
Isn't it to be able to do that last on-exit message about how many iterations it managed to do?
thanks for this! just what i was looking for; handy script
Doesn't work for me. I can CTRL + C and it doesn't re-run the .py script if it throws an error.