Take the 2-minute tour ×
Unix & Linux Stack Exchange is a question and answer site for users of Linux, FreeBSD and other Un*x-like operating systems.. It's 100% free, no registration required.

There's a builtin 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, rosetta stone style.

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."
share|improve this question
1  
Why not: repeat [-t time] [-n number] command [args...]? –  Jonathan Leffler Feb 17 '09 at 0:22
10  
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 '09 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. –  Rory Dec 21 '09 at 13:02
4  
repeat is a builtin command in csh and tcsh. –  Keith Thompson Jan 12 '12 at 9:25
2  
@KeithThompson, csh, tcsh and zsh. Though it's more a keyword than a builtin –  Stephane Chazelas Oct 21 '12 at 9:54
show 2 more comments

migrated from stackoverflow.com Apr 5 '11 at 9:44

This question came from our site for professional and enthusiast programmers.

22 Answers

Bash

while + sleep:

while true
do 
    echo "Hi"
    sleep 1
done
share|improve this answer
    
Cool, thanks; which shell is that? But note that I'm looking for something you execute as a single command-line command. –  dreeves Feb 17 '09 at 0:19
1  
I believe it works as written in a generic Bourne shell, as well. –  dmckee Feb 17 '09 at 0:24
2  
@dreeves, using ";" as separator, commands can be execeted in a single line. Look at Toony's answer as a example –  bbaja42 Jan 12 '12 at 9:00
17  
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 –  David Costa Jan 12 '12 at 10:46
add comment

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); 
}
share|improve this answer
add comment

I like the scripts, but an alternate approach would be to use cron...at least for n = m*60

::blushes:: Hangs head in shame. Thanks Jonathan.

share|improve this answer
1  
cron doesn't work at second granularities - minutes are as small as it works with. –  Jonathan Leffler Feb 17 '09 at 0:21
    
Aye, there's that... –  dmckee Feb 17 '09 at 0:22
1  
With the single-line shell answers, the disadvantage is if they die, it just stops forever. With cron, it starts a new one each time. –  kurtm Oct 17 '13 at 17:32
add comment

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, forever.

On Mac OS X, you can get watch from Mac Ports, or you can get it via Homebrew:

brew install watch
share|improve this answer
    
Aha, thanks! I don't have that on my system (Mac OS X) -- do you have a link? –  dreeves Feb 17 '09 at 0:24
2  
you can use MacPorts to install it. sudo port install watch –  pope Feb 17 '09 at 0:27
    
fink has a package... –  dmckee Feb 17 '09 at 0:27
7  
Also available in homebrew (brew install watch). –  Lyle May 12 '11 at 21:45
6  
+1 For a new powerful tool. I used to while true; do X; sleep Y; done - This is way better. –  Adam Matan May 16 '11 at 11:14
show 3 more comments

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}'
share|improve this answer
15  
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 '11 at 10:17
2  
@Mikel: Or while : –  Keith Thompson Jan 12 '12 at 9:27
add comment

Care to try this out (Bash)?

forever ()   {
    TIMES=shift;
    SLEEP=shift;
    if [ "$TIMES" = "-1" ]; then  
        while true;
        do 
            $@
            sleep $SLEEP
        done
    else
        repeat "$TIMES" $@ 
    fi; }
share|improve this answer
    
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 '09 at 0:40
2  
repeat is specific to csh and tcsh. –  Keith Thompson Jan 12 '12 at 9:42
add comment

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 ;)

share|improve this answer
3  
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 '11 at 10:19
add comment

You could source this recursive function:

#!/bin/bash
ininterval () {
    delay=$1
    shift
    $*
    sleep $delay
    ininterval $delay $*
}

or add an:

ininterval $*

and call the script.

share|improve this answer
add comment

This solution works in MacOSX 10.7. 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.

share|improve this answer
    
Did you mean to have a sleep command there? –  Keith Thompson Jan 12 '12 at 9:41
3  
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. –  Keith Thompson Jan 17 '12 at 8:11
add comment

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?)
}
share|improve this answer
    
See also this other question –  Stephane Chazelas Oct 21 '12 at 9:52
add comment

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?

share|improve this answer
6  
Eval!? What for? sleep $1; shift; "$@" or similar would be much better. –  Mikel Apr 20 '12 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 '13 at 9:07
add comment

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.

share|improve this answer
add comment

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

share|improve this answer
    
Why the downvote? You can see this is a working solution by copy and pasting the example into a terminal window. –  Thomas Bratt Jan 27 '13 at 14:20
    
I think you got down voted because it is a little bit dangerous and consumes the CPU. –  ojblass Jul 6 '13 at 20:22
add comment

... 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!

share|improve this answer
1  
This is not correct. */60 is going to run every 60 minutes, and */5, for example, every 5 minutes. –  mika Oct 2 '13 at 17:47
add comment

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
share|improve this answer
    
Yes. This should be documented next to UUOC. –  Johan Mar 15 '13 at 9:05
3  
And if you want it to operate like "watch" you can do while clear; do date; command;sleep 5; done –  Johan Mar 15 '13 at 9:12
add comment

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:

repet=10
delay=1

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

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

share|improve this answer
add comment

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.

share|improve this answer
add comment

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
}
share|improve this answer
    
Note: it does not work with floats, despite sleep does accept them. –  Baronsed Nov 11 '13 at 22:37
add comment
#! /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
share|improve this answer
add comment

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).

# 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.

share|improve this answer
add comment

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.

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

Depending on granularity and duration of submited command...

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

If delay is more important and your command don'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 you're 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.

share|improve this answer
    
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 –  F. Hauri Jan 29 at 17:34
add comment

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.
share|improve this answer
add comment

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.