647

There's a built-in Unix command repeat whose first argument is the number of times to repeat a command, where the command (with any arguments) is specified by the remaining arguments to repeat.

For example,

% repeat 100 echo "I will not automate this punishment."

will echo the given string 100 times and then stop.

I'd like a similar command – let's call it forever – that works similarly except the first argument is the number of seconds to pause between repeats, and it repeats forever. For example,

% forever 5 echo "This will get echoed every 5 seconds forever and ever."

I thought I'd ask if such a thing exists before I write it. I know it's like a 2-line Perl or Python script, but maybe there's a more standard way to do this. If not, feel free to post a solution in your favorite scripting language.

PS: Maybe a better way to do this would be to generalize repeat to take both the number of times to repeat (with -1 meaning infinity) and the number of seconds to sleep between repeats. The above examples would then become:

% repeat 100 0 echo "I will not automate this punishment."
% repeat -1 5 echo "This will get echoed every 5 seconds forever."
10
  • 1
    Why not: repeat [-t time] [-n number] command [args...]? Feb 17, 2009 at 0:22
  • 23
    Cool. Unfortunately both on my Ubuntu and my AIX repeat is command not found. Can you do a type repeat and let me know where's coming from?
    – Davide
    Nov 22, 2009 at 23:47
  • 3
    I'm also on Ubuntu and don't have repeat installed. Any information on how to install this would be helpful. Dec 21, 2009 at 13:02
  • 9
    repeat is a builtin command in csh and tcsh. Jan 12, 2012 at 9:25
  • 4
    @KeithThompson, csh, tcsh and zsh. Though it's more a keyword than a builtin Oct 21, 2012 at 9:54

24 Answers 24

816

Try the watch command.

Usage: watch [-dhntv] [--differences[=cumulative]] [--help] [--interval=<n>] 
             [--no-title] [--version] <command>`

So that:

watch -n1  command

will run the command every second (well, technically, every one second plus the time it takes for command to run as watch (at least the procps and busybox implementations) just sleeps one second in between two runs of command), forever.

Would you want to pass the command to exec instead of sh -c, use -x option:

watch -n1 -x command

On macOS, you can get watch from Mac Ports:

port install watch

Or you can get it from Homebrew:

brew install watch
16
  • 3
    you can use MacPorts to install it. sudo port install watch
    – pope
    Feb 17, 2009 at 0:27
  • 12
    Also available in homebrew (brew install watch).
    – Lyle
    May 12, 2011 at 21:45
  • 43
    +1 For a new powerful tool. I used to while true; do X; sleep Y; done - This is way better.
    – Adam Matan
    May 16, 2011 at 11:14
  • 12
    Problem with this tool (on Ubuntu at least) is that it forces fullscreen, so if I'm dumping some periodic info, I lose the previous info. Oct 12, 2012 at 1:00
  • 5
    It is called cmdwatch on FreeBSD (from the ports: sysutils/cmdwatch).
    – Ouki
    Mar 25, 2014 at 21:29
348

Bash

while + sleep:

while true
do 
    echo "Hi"
    sleep 1
done

Here's the same thing as a shorthand one-liner (From the comments below):

while sleep 1; do echo "Hi"; done

Uses ; to separate commands and uses sleep 1 for the while test since it always returns true. You can put more commands in the loop - just separate them with ;

9
  • 3
    I believe it works as written in a generic Bourne shell, as well. Feb 17, 2009 at 0:24
  • 5
    @dreeves, using ";" as separator, commands can be execeted in a single line. Look at Toony's answer as a example
    – bbaja42
    Jan 12, 2012 at 9:00
  • 67
    sleep 1 always returns true so you can use it as the while condition. Not the most readable thing in the world but absolutely legit: while sleep 1; do echo "Hi"; done Jan 12, 2012 at 10:46
  • 10
    @David Costa: You made a reasonable point, but sleep doesn’t always return true. Ī̲ used such loops (albeit with longer delay) namely because there is a way to terminate it gracefully with killing the sleep process. Sep 12, 2015 at 22:53
  • 4
    @IncnisMrsi I didn't think of it, it actually returns zero only when the time has passed and non-zero when it is terminated by a signal. Thanks for pointing it out Sep 13, 2015 at 9:58
101

This is just a shorter version of other while+sleep answers, if you are running this kind of tasks often as your daily routine, using this saves you from unnecessary key presses, and if your command line starts to get longer understanding this one is a bit easier. But this one starts with sleeping first.

This is generally useful if you need to follow something has one-line output like machine load:

while sleep 1; do uptime; done
4
  • Yes. This should be documented next to UUOC.
    – Johan
    Mar 15, 2013 at 9:05
  • 15
    And if you want it to operate like "watch" you can do while clear; do date; command;sleep 5; done
    – Johan
    Mar 15, 2013 at 9:12
  • 1
    Wow, thanks for while sleep combination, saves keystokes!
    – George Y.
    Jun 5, 2016 at 2:34
  • while sleep ... is a good solution if you want colored output. watch does not provide colored output by default (I'm sure there is some way to get it, but this answer is fast and easy.)
    – BRasmussen
    Nov 19, 2023 at 17:05
48

One problem that all the answers posted so far have is that the time the command is executed can drift. For example, if you do a sleep 10 between commands, and the command takes 2 seconds to run, then it's going to run every 12 seconds; if it takes a variable amount of time to run, then over the long term the time when it runs can be unpredictable.

This might be just what you want; if so, use one of the other solutions, or use this one but simplify the sleep call.

For one-minute resolution, cron jobs will run at the specified time, regardless of how long each command takes. (In fact a new cron job will launch even if the previous one is still running.)

Here's a simple Perl script that sleeps until the next interval, so for example with an interval of 10 seconds the command might run at 12:34:00, 12:34:10, 12:34:20, etc., even if the command itself takes several seconds. If the command runs more than interval seconds, the next interval will be skipped (unlike cron). The interval is computed relative to the epoch, so an interval of 86400 seconds (1 day) will run at midnight UTC.

#!/usr/bin/perl

use strict;
use warnings;

if (scalar @ARGV < 2) {
    die "Usage: $0 seconds command [args...]\n";
}

$| = 1;  # Ensure output appears

my($interval, @command) = @ARGV;

# print ">>> interval=$interval command=(@command)\n";

while (1) {
    print "sleep ", $interval - time % $interval, "\n";
    sleep $interval - time % $interval;
    system @command; # TODO: Handle errors (how?)
}
3
  • See also this other question Oct 21, 2012 at 9:52
  • 2
    simpler in bash to minimize drift (though it may drift over very long usage windows due to the sleep command and bash itself accumulating its tiny execution times): while :; do sleep 1m & command; wait; done
    – math
    Sep 14, 2017 at 13:53
  • 1
    @math: Clever. It doesn't work if you have other background processes running in the current shell (wait will wait for all of them). That's fixable by changing wait to wait $!, where $! is the PID of the sleep process. Post this as an answer and I'll upvote it. But it does produce some extraneous output in an interactive shell. Sep 14, 2017 at 20:59
44

I think all the answer here so far are either too convoluted, or instead answer a different question:

  • "How to run a program repeatedly so that there are X seconds delay between when the program finished, and the next starts".

The real question was:

  • "How to run a program every X seconds"

These are two very different things when the command takes time to finish.

Take for instance the script foo.sh (pretend this is a program that takes a few seconds to complete).

#!/bin/bash
# foo.sh
echo `date +"%H:%M:%S"` >> output.txt;
sleep 2.5;
# ---

You wish to run this every second, and most would suggest watch -n1 ./foo.sh, or while sleep 1; do ./foo.sh; done. However, this gives the output:

15:21:20
15:21:23
15:21:27
15:21:30
15:21:34

Which is not exactly being run every second. Even with the -p flag, as the man watch page suggests might solve this, the result is the same.

An easy way to accomplish the desired task, which some touch on, is to run the command in the background. In other words:

while sleep 1; do (./foo.sh &) ; done

And that is all there is to it.

You could run it every 500 ms with sleep 0.5, or what have you.

5
  • 1
    Those who voted on different answers, do not understand the elegance of your answer... Jun 26, 2015 at 22:58
  • 2
    This is good unless your program has side effects. If the program takes longer than the interval, the side effects can interfere (like overwriting a file). If the program waits for something else, and that something else is taking forever, then you're starting more and more background jobs indefinitely. Use with caution. Jul 16, 2015 at 20:36
  • 9
    could invert the order of the backgrounding to ensure that not more than one ./foo.sh is run at a time, but no faster than 1 per second: while :; do sleep 1 & ./foo.sh; wait; done
    – math
    Sep 14, 2017 at 13:57
  • 2
    Yes, it will drift, a few milliseconds on each loop, but it will. Make date +"%H:%M:%S.%N" in foo.sh to see how the fraction will increase slowly on each loop.
    – user232326
    Jul 24, 2018 at 12:53
  • Yes, it's true that most answers don't literally address the question. But it's an almost safe bet that they answer what the OP wanted. It would be a safer bet if the OP had actually accepted an answer... But this is good, provided you are aware of possible side effects and you are certain that the average run time of the command is not going to exceed the cycle time
    – Auspex
    Jul 26, 2018 at 11:17
17

In bash:

bash -c 'while [ 0 ]; do echo "I will not automate this punishment in absurdum."; done'

(echo could be replaced by any command...

Or in perl:

perl -e 'for (;1;) {print "I will not automate this punishment in absurdum.\n"}'

Where print "I will not automate this punishment in absurdum.\n" could be replaced with "any" command surrounded with backticks (`).

And for a pause, add a sleep statement inside the for loop:

bash -c 'while [ 0 ]; do echo "I will not automate this punishment in absurdum."; sleep 1; done'

and

perl -e 'for (;1;) {print "I will not automate this punishment in absurdum.\n"; sleep 1}'
2
  • 18
    while [ 0 ] is a bad way to write it. It means 0 is a string with length > 0. while [ 1 ] or while [ jeff ] would do the same thing. Better to write while true.
    – Mikel
    Apr 5, 2011 at 10:17
  • 7
    @Mikel: Or while : Jan 12, 2012 at 9:27
12

Recent bash >= 4.2 under recent Linux kernel, based answer.

In order to limit execution time, there is no forks! Only built-in are used.

For this, I use read builtin function instead of sleep. Unfortunely this won't work with notty sessions.

Quick function "repeat" as requested:

repeat () {
   local repeat_times=$1 repeat_delay=$2 repeat_foo repeat_sleep
   read -t .0001 repeat_foo
   if [ $? = 1 ] ;then
       repeat_sleep() { sleep $1 ;}
   else
       repeat_sleep() { read -t $1 repeat_foo; }
   fi
   shift 2
   while ((repeat_times)); do
        ((repeat_times=repeat_times>0?repeat_times-1:repeat_times))
        "${@}"
        ((repeat_times))&& ((10#${repeat_delay//.})) &&
            repeat_sleep $repeat_delay
   done
}

Little test with quoted strings:

repeat 3 0 printf "Now: %(%T)T, Hello %s.\n" -1 Guy
Now: 15:13:43, Hello Guy.
Now: 15:13:43, Hello Guy.
Now: 15:13:43, Hello Guy.

repeat -1 .5 printf "Now: %(%T)T, Hello %s.\n" -1 Guy
Now: 15:14:14, Hello Guy.
Now: 15:14:14, Hello Guy.
Now: 15:14:15, Hello Guy.
Now: 15:14:15, Hello Guy.
Now: 15:14:16, Hello Guy.
Now: 15:14:16, Hello Guy.
Now: 15:14:17, Hello Guy.
Now: 15:14:17, Hello Guy.
Now: 15:14:18, Hello Guy.
Now: 15:14:18, Hello Guy.
^C

Depending on granularity and duration of submitted command...

Under recent Linux kernels, there is a procfile /proc/timer_list containing time information in nanoseconds.

If you want to run a command exactly once by second, your command has to end in less than a second! And from there, you have to sleep only the rest of current second.

If the delay is more important and your command doesn't require significant time, you could:

command=(echo 'Hello world.')
delay=10
while :;do
    printf -v now "%(%s)T" -1
    read -t $(( delay-(now%delay) )) foo
    ${command[@]}
  done.

But if your goal is to obtain finer granularity, you have to:

Use nanoseconds information to wait until begin of a second...

For this, I wrote a little bash function:

# bash source file for nano wait-until-next-second

mapfile  </proc/timer_list _timer_list
for ((_i=0;_i<${#_timer_list[@]};_i++));do
    ((_c+=${#_timer_list[_i]}))
    [[ ${_timer_list[_i]} =~ ^now ]] && TIMER_LIST_READ=$_c
    [[ ${_timer_list[_i]} =~ offset:.*[1-9] ]] && \
        TIMER_LIST_OFFSET=${_timer_list[_i]//[a-z.: ]} && \
        break
done
unset _i _timer_list _c
readonly TIMER_LIST_OFFSET TIMER_LIST_READ
waitNextSecondHires() {
    local nsnow nsslp
    read -N$TIMER_LIST_READ nsnow </proc/timer_list
    nsnow=${nsnow%% nsecs*}
    nsnow=$((${nsnow##* }+TIMER_LIST_OFFSET))
    nsslp=$((2000000000-10#${nsnow:${#nsnow}-9}))
    read -t .${nsslp:1} foo
}

After sourcing them, you could:

command=(echo 'Hello world.')
while :;do
    waitNextSecondHires
    ${command[@]}
  done.

run ${command[@]} directly on command line, than compare to

command=(eval "echo 'Hello world.';sleep .3")
while :;do
    waitNextSecondHires
    ${command[@]}
  done.

this must give exactly same result.

Hires function "repeat" as requested:

You could source this:

mapfile  </proc/timer_list _timer_list
for ((_i=0;_i<${#_timer_list[@]};_i++));do
    ((_c+=${#_timer_list[_i]}))
    [[ ${_timer_list[_i]} =~ ^now ]] && TIMER_LIST_READ=$_c
    [[ ${_timer_list[_i]} =~ offset:.*[1-9] ]] && \
        TIMER_LIST_OFFSET=${_timer_list[_i]//[a-z.: ]} && \
        break
done
unset _i _timer_list _c
readonly TIMER_LIST_OFFSET TIMER_LIST_READ

repeat_hires () {
    local repeat_times=$1 repeat_delay=$2 repeat_foo repeat_sleep repeat_count
    read -t .0001 repeat_foo
    if [ $? = 1 ] ;then
        repeat_sleep() { sleep $1 ;}
    else
        repeat_sleep() { read -t $1 repeat_foo; }
    fi
    shift 2
    printf -v repeat_delay "%.9f" $repeat_delay
    repeat_delay=${repeat_delay//.}
    read -N$TIMER_LIST_READ nsnow </proc/timer_list
    nsnow=${nsnow%% nsec*}
    started=${nsnow##* }
    while ((repeat_times)); do
        ((repeat_times=repeat_times>0?repeat_times-1:repeat_times))
        "${@}"
        ((repeat_times)) && ((10#$repeat_delay)) && {
            read -N$TIMER_LIST_READ nsnow </proc/timer_list
            nsnow=${nsnow%% nsec*}
            nsnow=${nsnow##* }
            (( (nsnow - started) / 10#$repeat_delay - repeat_count++ )) &&
                printf >&2 "WARNING: Command '%s' too long for %f delay.\n" \
                           "${*}" ${repeat_delay:0:${#repeat_delay}-9
                           }.${repeat_delay:${#repeat_delay}-9}
            printf -v sleep "%010d" $((
                10#$repeat_delay - ( ( nsnow - started ) % 10#$repeat_delay ) ))
            repeat_sleep ${sleep:0:${#sleep}-9}.${sleep:${#sleep}-9}
        }
    done
}

Then try it:

time repeat_hires 21 .05 sh -c 'date +%s.%N;sleep .01'
1480867565.152022457
1480867565.201249108
1480867565.251333284
1480867565.301224905
1480867565.351236725
1480867565.400930482
1480867565.451207075
1480867565.501212329
1480867565.550927738
1480867565.601199721
1480867565.651500618
1480867565.700889792
1480867565.750963074
1480867565.800987954
1480867565.853671458
1480867565.901232296
1480867565.951171898
1480867566.000917199
1480867566.050942638
1480867566.101171249
1480867566.150913407

real    0m1.013s
user    0m0.000s
sys     0m0.016s

time repeat_hires 3 .05 sh -c 'date +%s.%N;sleep .05'
1480867635.380561067
WARNING: Command 'sh -c date +%s.%N;sleep .05' too long for 0.050000 delay.
1480867635.486503367
WARNING: Command 'sh -c date +%s.%N;sleep .05' too long for 0.050000 delay.
1480867635.582332617

real    0m0.257s
user    0m0.000s
sys     0m0.004s
1
  • read -t is a built-in, while sleep is not. This could have border effect (ie hitting [key:return] interrupt quietly the sleep), but this could be treated by testing $foo Jan 29, 2014 at 17:34
5

If your intention is not to display a message to your screen, and if you could afford to repeat the job in terms of minutes, crontab, perhaps, would be your best tool. For example, if you wish to execute your command every minute, you would write something like this in your crontab file:

* * * * * my_precious_command

Please check out the tutorial for further example. Also, you can set the timings easily using Crontab Code Generator.

5

Bash and some of its kindred shells have the convenient (( ... )) notation wherein arithmetic expressions can be evaluated.

So as an answer to your third challenge, where both the repeat count and delay between each repeat should be configurable, here's one way to do it:

repeat=10
delay=1

i=0
while (( i++ < repeat )); do
  echo Repetition $i
  sleep $delay
done

This answer also suffers from the timing drift covered in Keith's answer.

0
4

Perl

#!/usr/bin/env perl
# First argument is number of seconds to sleep between repeats, remaining
# arguments give the command to repeat forever.

$sleep = shift;
$cmd = join(' ', @ARGV);

while(1) {
  system($cmd);
  sleep($sleep); 
}
4

What if we had both ?

Here's the idea : with only "interval", it repeats forever. With "interval" and "times", it repeats this number of times, separated by "interval".

The usage :

$ loop [interval [times]] command

So, the algorithm will be :

  • List item
  • if $1 contains only digits, it is the interval (default 2)
  • if $2 contains only digits, it is the number of times (default infinite)
  • while loop with these parameters
    • sleep "interval"
    • if a number of times has been given, decrement a var until it is reached

Therefore :

loop() {
    local i=2 t=1 cond

    [ -z ${1//[0-9]/} ] && i=$1 && shift
    [ -z ${1//[0-9]/} ] && t=$1 && shift && cond=1
    while [ $t -gt 0 ]; do 
        sleep $i
        [ $cond ] && : $[--t]
        $@
    done
}
1
  • Note: it does not work with floats, despite sleep does accept them.
    – Baronsed
    Nov 11, 2013 at 22:37
4

Try this out (Bash):

forever ()   {
    TIMES=shift;
    SLEEP=shift;
    if [ "$TIMES" = "-1" ]; then
        while true;
        do
            $@
            sleep $SLEEP
        done
    else
        repeat "$TIMES" $@
    fi; }
3
  • I'm not to hot at shell, so improvements welcome. And I don't seem to have a "repeat" command in my distro.
    – Gregg Lind
    Feb 17, 2009 at 0:40
  • 3
    repeat is specific to csh and tcsh. Jan 12, 2012 at 9:42
  • 2
    @KeithThompson: and zsh
    – Thor
    Aug 2, 2017 at 9:57
3

As mentioned by gbrandt, if the watch command is available, definitely use it. Some Unix systems, however, don't have it installed by default (at least they don't where I work).

Here's another solution with slightly different syntax and output (works in BASH and SH):

while [ 1 ] ; do
    <cmd>
    sleep <x>
    echo ">>>>>>>>>>>>>" `date` ">>>>>>>>>>>>>>"
done

Edit: I removed some "." in the last echo statement...holdover from my Perl days ;)

1
  • 4
    while [ 1 ] only works because 1 is treated as a string, just like while [ -n 1 ]. while [ 0 ] or while [ jeff ] would do the same thing. while true makes much more sense.
    – Mikel
    Apr 5, 2011 at 10:19
3

You can obtain the power from python interpreter from shell.

python3 -c "import time, os
while True:
    os.system('your command')
    time.sleep(5)

replace five in any time(sec) you like.

Attention: the return use SHIFT+ENTER to return input in shell. And do not forget to type 4 SPACE indent in each line in while loop.

2
  • 1
    Note that python's os.system() executes a shell to interpret the command line, so invoking python to run shells in a loop to run a command periodically is a bit overkill. You might as well do everything in the shell. Jun 26, 2018 at 11:26
  • I agree with you. However, there is a 5 second sleep as you described in each loop. Thus the extra cost from starting a new shell can be ignored. If this cost make the period longer than expectation, you can reduce some sleep time as requirement.
    – pah8J
    Jun 27, 2018 at 8:09
2

I wound up up creating a variant on swalog's answer. With his you had to wait X seconds for the first iteration, I'm also running my in the foreground so..

./foo.sh;while sleep 1; do (./foo.sh) ; done
1
  • What do the parens around ./foo.sh do? And why the additional ./foo.sh in the front? Dec 16, 2022 at 18:15
1

Quick, dirty and probably dangerous to boot, but if you're adventurous and know what you're doing, put this into repeat.sh and chmod 755 it,

while true
do 
    eval $1 
    sleep $2 
done

Invoke it with ./repeat.sh <command> <interval>

My spidey sense says this is probably an evil way of doing this, is my spidey sense right?

2
  • 8
    Eval!? What for? sleep $1; shift; "$@" or similar would be much better.
    – Mikel
    Apr 20, 2012 at 2:01
  • Dunno why this is -2. eval may be overkill... except when you need it. Eval lets you get things like redirection onto the command line.
    – Johan
    Mar 15, 2013 at 9:07
1

You could run a script from init (adding a line to /etc/inittab). This script must run your command, sleep for the time you want to wait until run the script again, and exit it. Init will start your script again after exit.

0

To repeatedly run a command in a console window I usually run something like this:

while true; do (run command here); done

This works for multiple commands as well, for example, to display a continually updating clock in a console window:

while true; do clear; date; sleep 1; done

2
  • Why the downvote? You can see this is a working solution by copy and pasting the example into a terminal window. Jan 27, 2013 at 14:20
  • I think you got down voted because it is a little bit dangerous and consumes the CPU.
    – ojblass
    Jul 6, 2013 at 20:22
0
#! /bin/sh

# Run all programs in a directory in parallel
# Usage: run-parallel directory delay
# Copyright 2013 by Marc Perkel
# docs at http://wiki.junkemailfilter.com/index.php/How_to_run_a_Linux_script_every_few_seconds_under_cron"
# Free to use with attribution

if [ $# -eq 0 ]
then
   echo
   echo "run-parallel by Marc Perkel"
   echo
   echo "This program is used to run all programs in a directory in parallel" 
   echo "or to rerun them every X seconds for one minute."
   echo "Think of this program as cron with seconds resolution."
   echo
   echo "Usage: run-parallel [directory] [delay]"
   echo
   echo "Examples:"
   echo "   run-parallel /etc/cron.20sec 20"
   echo "   run-parallel 20"
   echo "   # Runs all executable files in /etc/cron.20sec every 20 seconds or 3 times a minute."
   echo 
   echo "If delay parameter is missing it runs everything once and exits."
   echo "If only delay is passed then the directory /etc/cron.[delay]sec is assumed."
   echo
   echo 'if "cronsec" is passed then it runs all of these delays 2 3 4 5 6 10 12 15 20 30'
   echo "resulting in 30 20 15 12 10 6 5 4 3 2 executions per minute." 
   echo
   exit
fi

# If "cronsec" is passed as a parameter then run all the delays in parallel

if [ $1 = cronsec ]
then
   $0 2 &
   $0 3 &
   $0 4 &
   $0 5 &
   $0 6 &
   $0 10 &
   $0 12 &
   $0 15 &
   $0 20 &
   $0 30 &
   exit
fi

# Set the directory to first prameter and delay to second parameter

dir=$1
delay=$2

# If only parameter is 2,3,4,5,6,10,12,15,20,30 then automatically calculate 
# the standard directory name /etc/cron.[delay]sec

if [[ "$1" =~ ^(2|3|4|5|6|10|12|15|20|30)$ ]]
then
   dir="/etc/cron.$1sec"
   delay=$1
fi

# Exit if directory doesn't exist or has no files

if [ ! "$(ls -A $dir/)" ]
then
   exit
fi

# Sleep if both $delay and $counter are set

if [ ! -z $delay ] && [ ! -z $counter ]
then
   sleep $delay
fi

# Set counter to 0 if not set

if [ -z $counter ]
then
   counter=0
fi

# Run all the programs in the directory in parallel
# Use of timeout ensures that the processes are killed if they run too long

for program in $dir/* ; do
   if [ -x $program ] 
   then
      if [ "0$delay" -gt 1 ] 
      then
         timeout $delay $program &> /dev/null &
      else
         $program &> /dev/null &
      fi
   fi
done

# If delay not set then we're done

if [ -z $delay ]
then
   exit
fi

# Add delay to counter

counter=$(( $counter + $delay ))

# If minute is not up - call self recursively

if [ $counter -lt 60 ]
then
   . $0 $dir $delay &
fi

# Otherwise we're done
0

Easy way to repeat a job from crontab with an interval less than one minute (20 seconds example) :

crontab: * * * * * script.sh

script.sh:

#!/bin/bash
>>type your commands here.

sleep 20
>>retype your commands here.

sleep 20
>>retype your commands here.
1
  • Have you heard of loops? Invented about 60 years ago or more. Dec 16, 2022 at 18:12
0

With zsh and a sleep implementation that accepts floating-point arguments:

typeset -F SECONDS=0 n=0
repeat 100 {cmd; sleep $(((n+=3) - SECONDS))}

Or for forever:

for ((;;)) {cmd; sleep $(((n+=3) - SECONDS))}

If your sleep doesn't support floats, you can always redefine it as a wrapper around zsh's zselect builtin:

zmodload zsh/zselect
sleep() zselect -t $((($1 * 100) | 0))
0

This solution works on Mac OS X v10.7 (Lion). It works wonderfully.

bash -c 'while [ 0 ]; do \
      echo "I will not automate this punishment in absurdum."; done'

In my case

bash -c 'while [ 0 ]; do ls; done'

or

bash -c 'while [ 0 ]; do mv "Desktop/* Documents/Cleanup"; done'

to clean up my desktop constantly.

2
  • 2
    Did you mean to have a sleep command there? Jan 12, 2012 at 9:41
  • 6
    while [ 0 ] is an odd way to write an infinite loop; it works because the test command (also known as [) treats a non-empty string as true. while : ; do ... ; done is more idiomatic, or if you prefer you can use while true ; do ... ; done. Jan 17, 2012 at 8:11
-1
until ! sleep 60; do echo $(date); command; command; command; done

works for me.

  1. I don´t need it exactly every 60 seconds
  2. "watch" does not watch commands on all systems
  3. "watch" can take only one command (or a script file)
  4. putting the sleep in the condition instead of the body makes the loop better interruptable (so a claim in a reply to a similar question on stackoverflow. unfortunately I can find it at the moment)
  5. I want to execute it once before the delay, so I use until instead of while
1
  • I just noticed that "until" apparently executes the sleep before the first iteration too. is there any way of putting the condition at the end of the loop in bash?
    – Titus
    Jun 11, 2016 at 13:04
-2

... I wonder how much complicated solutions can be created when solving this problem.

It can be so easy...

open /etc/crontab 

put there 1 next line to the end of file like:

*/NumberOfSeconds * * * * user /path/to/file.sh

If you want to run something every 1 second, just put there:

*/60 * * * * root /path/to/file.sh 

where that file.sh could be chmod 750 /path/to/file.sh

and inside of that file.sh should be:

#!/bin/bash 
#What does it do
#What is it runned by

your code or commands

and thats all!

ENJOY!

2
  • 7
    This is not correct. */60 is going to run every 60 minutes, and */5, for example, every 5 minutes.
    – mika
    Oct 2, 2013 at 17:47
  • 2
    seconds is not allowed on crontab
    – GAD3R
    Nov 30, 2017 at 12:12

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .