475

I occasionally run a bash command line like this:

n=0; while [[ $n -lt 10 ]]; do some_command; n=$((n+1)); done

To run some_command a number of times in a row -- 10 times in this case.

Often some_command is really a chain of commands or a pipeline.

Is there a more concise way to do this?

jww
  • 97,681
  • 90
  • 411
  • 885
bstpierre
  • 30,042
  • 15
  • 70
  • 103

20 Answers20

732

If your range has a variable, use seq, like this:

count=10
for i in $(seq $count); do
    command
done

Simply:

for run in {1..10}; do
  command
done

Or as a one-liner, for those that want to copy and paste easily:

for run in {1..10}; do command; done
bryant1410
  • 5,540
  • 4
  • 39
  • 40
Joe Koberg
  • 25,416
  • 6
  • 48
  • 54
  • 29
    If you have LOTS of iterations, the form with `for (n=0;n – Joe Koberg Sep 17 '10 at 18:07
  • @Joe Koberg, thanks for the tip. I'm typically use N<100 so this seems good. – bstpierre Sep 17 '10 at 18:11
  • Accepting this as the answer since it is slightly more concise (by a whisker) than `for(())`. – bstpierre Sep 17 '10 at 18:35
  • 12
    @bstpierre: The brace expansion form can't use variables (easily) to specify the range in Bash. – Dennis Williamson Sep 17 '10 at 19:02
  • 6
    That is true. Brace expansion is performed before variable expansion according to http://www.gnu.org/software/bash/manual/bashref.html#Brace-Expansion , thus it will never see the values of any variables. – Joe Koberg Sep 17 '10 at 19:10
  • @Dennis Williamson - Thanks for the warning, and for your answer below. 99% of the time I won't need to worry about it. – bstpierre Sep 18 '10 at 03:36
  • 5
    Sad thing about variable expansion. I am using following to loop n-times and have formated numbers: `n=15;for i in $(seq -f "%02g" ${n});do echo $i; done 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15` – user1830432 Aug 15 '14 at 09:35
  • Ali Omary's answer (https://stackoverflow.com/a/26898191/1727807) is the only one which doesn't use an external tool or have possible side effects, since '_' should never be used to store persistent data. Using `run` as your variable will result in `run` being set after the `for` loop. If you're in a function, you can also use `local run` before the loop to avoid overwriting a variable which may be set outside the function. – Hart Simha Jan 20 '17 at 21:10
  • I am using a Mac and CTRL-C won't be able to stop it... only one execution is stopped and it will keep running the loop – nonopolarity Mar 08 '20 at 16:26
239

Using a constant:

for ((n=0;n<10;n++)); do
    some_command; 
done

Using a variable (can include math expressions):

x=10; for ((n=0; n < (x / 2); n++)); do some_command; done
Nam G VU
  • 33,193
  • 69
  • 233
  • 372
BatchyX
  • 4,986
  • 2
  • 18
  • 17
  • 5
    This is the closest way to programming language ^^ - should be the accepted one! – Nam G VU Jul 20 '17 at 07:05
  • This one is better because it's a single line and therefore easier to use within terminal – Kunok Jan 05 '18 at 22:09
  • You can also use a variable, e.g. `x=10` `for ((n=0; n < $x; n++));` – Jelphy Jun 05 '18 at 13:17
  • 2
    @Kunok It's just as acceptable to run the accepted answer in one line: `for run in {1..10}; do echo "on run $run"; done` – Douglas Adams Jun 05 '18 at 21:08
  • what if `n` is already set to a specific value? `for (( ; n<10; n++ ))` doesnt work / edit: best probably use one of the other answers like the `while (( n++...` one – phil294 Dec 06 '19 at 20:35
164

Another simple way to hack it:

seq 20 | xargs -Iz echo "Hi there"

run echo 20 times.


Notice that seq 20 | xargs -Iz echo "Hi there z" would output:

Hi there 1
Hi there 2
...

Peter Neyens
  • 9,770
  • 27
  • 33
mitnk
  • 3,127
  • 2
  • 21
  • 28
  • 30
    version 2015: `seq 20 | xargs -I{} echo "Hi there {}"` – mitnk May 12 '15 at 02:52
  • 7
    Note that `z` is not a good placeholder because it's so common that can be a regular text in command text. Use `{}` will be better. – mitnk Oct 28 '15 at 02:19
  • 4
    Any way to discard the number from `seq` ? so it would just print `Hi there` 20 times (no number)? EDIT: just use `-I{}` and then do not have any `{}` in the command – Mike Graf Aug 24 '16 at 21:48
  • 1
    Just use something, you are sure it will not be in the output, for example `IIIII` then it looks nice for example: `seq 20 | xargs -IIIIII timeout 10 yourCommandThatHangs` – rubo77 May 27 '19 at 10:59
148

If you're using the zsh shell:

repeat 10 { echo 'Hello' }

Where 10 is the number of times the command will be repeated.

Wilson Silva
  • 10,046
  • 6
  • 26
  • 31
36

Using GNU Parallel you can do:

parallel some_command ::: {1..1000}

If you do not want the number as argument and only run a single job at a time:

parallel -j1 -N0 some_command ::: {1..1000}

Watch the intro video for a quick introduction: https://www.youtube.com/playlist?list=PL284C9FF2488BC6D1

Walk through the tutorial (http://www.gnu.org/software/parallel/parallel_tutorial.html). You command line with love you for it.

Ole Tange
  • 31,768
  • 5
  • 86
  • 104
  • 1
    Why would you use a tool whose very reason for existence, as manifestly proven by its name, is to run things *in parallel*, and then give it cryptic argments `-j1 -N0` that *turn off the parallelism*???? – Ross Presser Feb 28 '20 at 00:14
  • 1
    @RossPresser That is a very reasonable question. The reason is that it may be easier to express what you want to do using the GNU Parallel syntax. Think: How would you run `some_command` 1000 times in serial with no arguments? And: Can you express that shorter than `parallel -j1 -N0 some_command ::: {1..1000}`? – Ole Tange Feb 28 '20 at 06:22
  • Well, I guess you have sort of a point, but it still really feels like using a finely crafted engine block as a hammer. If I was sufficiently motivated, *and* felt considerate of my future successors trying to understand what I did, I'd probably wrap the confusion in a shell script and just call it with `repeat.sh repeatcount some_command`. For implementation in the shell script I might use `parallel`, if it was already installed on the machine (probably wouldn't be). Or I might gravitate towards xargs since that seems to be fastest when no redirection is needed by `some_command`. – Ross Presser Feb 28 '20 at 19:10
  • 1
    I apologize if my "very reasonable question" was expressed in an unreasonable manner. – Ross Presser Feb 28 '20 at 19:11
  • 2
    In this specific case it may not be quite so obvious. It becomes more obvious if you use more options of GNU Parallel, e.g if you want to retry failed commands 4 times and save the output to different files: `parallel -N0 -j1 --retries 4 --results outputdir/ some_command` – Ole Tange Feb 28 '20 at 19:28
  • OK, you've made the argument persuasive. I won't say I'm a `parallel` convert yet, but I may look it over this weekend. – Ross Presser Feb 28 '20 at 22:23
  • I highly recommend you follow the "Reader's guide" in `man parallel`. It should get you going in less than 15 minutes. – Ole Tange Feb 29 '20 at 08:52
23

A simple function in the bash config file (~/.bashrc often) could work well.

function runx() {
  for ((n=0;n<$1;n++))
    do ${*:2}
  done
}

Call it like this.

$ runx 3 echo 'Hello world'
Hello world
Hello world
Hello world
steel
  • 11,883
  • 7
  • 72
  • 109
  • 1
    Like it. Now if it could be cancelled with ctrl+c, that would be great – slashdottir Apr 03 '17 at 17:36
  • Why using this way to echo $RANDOM n times displays same number? – BladeMight Jan 29 '18 at 10:37
  • 4
    This works perfectly. The only thing I needed to change was instead of just doing `do ${*:2}` I added eval `do eval ${*:2}` to make sure that it would work for running commands which don't start with executables. For example, if you want to set an environment variable before running a command like `SOME_ENV=test echo 'test'`. – 5_nd_5 May 03 '18 at 15:34
  • here is a version that runs N times or until the first non zero exit code. it can be stopped with ctrl+c function runx() { success=0 for ((n=0;n<$1 && success==0;n++)) do ${*:2} success=$? done } – Csene Jul 04 '22 at 12:43
15

If you are OK doing it periodically, you could run the following command to run it every 1 sec indefinitely. You can put other custom checks in place to run it n number of times.

watch -n 1 some_command

If you wish to have visual confirmation of changes, append --differences prior to the ls command.

According to the OSX man page, there's also

The --cumulative option makes highlighting "sticky", presenting a running display of all positions that have ever changed. The -t or --no-title option turns off the header showing the interval, command, and current time at the top of the display, as well as the following blank line.

Linux/Unix man page can be found here

Pankaj Singhal
  • 15,283
  • 9
  • 47
  • 86
14
for _ in {1..10}; do command; done   

Note the underscore instead of using a variable.

gniourf_gniourf
  • 44,650
  • 9
  • 93
  • 104
cyninja
  • 146
  • 1
  • 3
14

Another form of your example:

n=0; while (( n++ < 10 )); do some_command; done
Dennis Williamson
  • 346,391
  • 90
  • 374
  • 439
12

xargs is fast:

#!/usr/bin/bash
echo "while loop:"
n=0; time while (( n++ < 10000 )); do /usr/bin/true ; done

echo -e "\nfor loop:"
time for ((n=0;n<10000;n++)); do /usr/bin/true ; done

echo -e "\nseq,xargs:"
time seq 10000 | xargs -I{} -P1 -n1 /usr/bin/true

echo -e "\nyes,xargs:"
time yes x | head -n10000 |  xargs -I{} -P1 -n1 /usr/bin/true

echo -e "\nparallel:"
time parallel --will-cite -j1 -N0 /usr/bin/true ::: {1..10000}

On a modern 64-bit Linux, gives:

while loop:

real    0m2.282s
user    0m0.177s
sys     0m0.413s

for loop:

real    0m2.559s
user    0m0.393s
sys     0m0.500s

seq,xargs:

real    0m1.728s
user    0m0.013s
sys     0m0.217s

yes,xargs:

real    0m1.723s
user    0m0.013s
sys     0m0.223s

parallel:

real    0m26.271s
user    0m4.943s
sys     0m3.533s

This makes sense, as the xargs command is a single native process that spawns the /usr/bin/true command multiple time, instead of the for and while loops that are all interpreted in Bash. Of course this only works for a single command; if you need to do multiple commands in each iteration the loop, it will be just as fast, or maybe faster, than passing sh -c 'command1; command2; ...' to xargs

The -P1 could also be changed to, say, -P8 to spawn 8 processes in parallel to get another big boost in speed.

I don't know why GNU parallel is so slow. I would have thought it would be comparable to xargs.

James Scriven
  • 7,784
  • 1
  • 32
  • 36
  • 1
    GNU Parallel does quite a lot of setup and bookkeeping: It supports programmable replacement strings, it buffers output so output from two parallel jobs are never mixed, it supports direction (You cannot do: xargs "echo >foo") and since it is not written in bash it has to spawn a new shell to do the redirection. The primary cause, however, is in the Perl module IPC::open3 - so any work on getting that faster will result in a faster GNU Parallel. – Ole Tange Jul 17 '16 at 14:54
8

For one, you can wrap it up in a function:

function manytimes {
    n=0
    times=$1
    shift
    while [[ $n -lt $times ]]; do
        $@
        n=$((n+1))
    done
}

Call it like:

$ manytimes 3 echo "test" | tr 'e' 'E'
tEst
tEst
tEst
bta
  • 43,959
  • 6
  • 69
  • 99
  • 3
    This will not work if the command to run is more complex than a simple command with no redirections. – chepner Sep 12 '13 at 14:57
  • You can make it work for more complex cases, but you may have to wrap the command in a function or script. – bta Oct 02 '13 at 21:57
  • 2
    The `tr` here is misleading and it's working on output of `manytimes`, not `echo`. I think you should remove it from sample. – Dmitry Ginzburg May 18 '15 at 13:14
7

xargs and seq will help

function __run_times { seq 1 $1| { shift; xargs -i -- "$@"; } }

the view :

abon@abon:~$ __run_times 3  echo hello world
hello world
hello world
hello world
Gilles 'SO- stop being evil'
  • 104,111
  • 38
  • 209
  • 254
firejox
  • 137
  • 2
  • 2
  • 2
    what does the shift and the double-dash in the command do? Is the 'shift' really necessary? – sebs Apr 23 '13 at 00:07
  • 2
    This will not work if the command is more complex than a simple command with no redirections. – chepner Sep 12 '13 at 14:58
  • Very slow, echo $RANDOM 50 times took 7 sec, and even so it displays same random number each run... – BladeMight Jan 29 '18 at 10:46
5

All of the existing answers appear to require bash, and don't work with a standard BSD UNIX /bin/sh (e.g., ksh on OpenBSD).

The below code should work on any BSD:

$ echo {1..4}
{1..4}
$ seq 4
sh: seq: not found
$ for i in $(jot 4); do echo e$i; done
e1
e2
e3
e4
$
cnst
  • 25,870
  • 6
  • 90
  • 122
5

I solved with this loop, where repeat is an integer that represents the loops's number

repeat=10
for n in $(seq $repeat); 
    do
        command1
        command2
    done
fvlgnn
  • 97
  • 2
  • 4
5

You can use this command to repeat your command 10 times or more

for i in {1..10}; do **your command**; done

for example

for i in {1..10}; do **speedtest**; done
Martin Brisiak
  • 3,872
  • 12
  • 37
  • 51
M.S Sarabandi
  • 51
  • 1
  • 1
4

Yet another answer: Use parameter expansion on empty parameters:

# calls curl 4 times 
curl -s -w "\n" -X GET "http:{,,,}//www.google.com"

Tested on Centos 7 and MacOS.

jcollum
  • 43,623
  • 55
  • 191
  • 321
  • This is interesting, can you provide some more details on why this works? – Hassan Mahmud Jun 07 '20 at 12:04
  • 1
    It's running the parameter expansion n times but since the parameter is empty it doesn't actually change what is run – jcollum Jun 08 '20 at 16:18
  • 1
    Searching for parameter expansion and curl has led to no results. I barely know curl. It would be awesome if you could point me to some article or perhaps another common name for this feature. Thanks. – Hassan Mahmud Jun 08 '20 at 18:45
  • 1
    I think this may help https://www.gnu.org/software/bash/manual/html_node/Shell-Parameter-Expansion.html – jcollum Jun 08 '20 at 22:54
2

For loops are probably the right way to do it, but here is a fun alternative:

echo -e {1..10}"\n" |xargs -n1 some_command

If you need the iteration number as a parameter for your invocation, use:

echo -e {1..10}"\n" |xargs -I@ echo now I am running iteration @

Edit: It was rightly commented that the solution given above would work smoothly only with simple command runs (no pipes, etc.). you can always use a sh -c to do more complicated stuff, but not worth it.

Another method I use typically is the following function:

rep() { s=$1;shift;e=$1;shift; for x in `seq $s $e`; do c=${@//@/$x};sh -c "$c"; done;}

now you can call it as:

rep 3 10 echo iteration @

The first two numbers give the range. The @ will get translated to the iteration number. Now you can use this with pipes too:

rep 1 10 "ls R@/|wc -l"

with give you the number of files in directories R1 .. R10.

stacksia
  • 631
  • 6
  • 10
  • 1
    This will not work if the command is more complex than a simple command with no redirections. – chepner Sep 12 '13 at 14:59
  • fair enough, but see my edit above. The edited method uses a sh -c, so should work with redirection too. – stacksia Sep 16 '13 at 13:52
  • `rep 1 2 touch "foo @"` will not create two files "foo 1" and "foo 2"; rather it creates three files "foo", "1", and "2". There may be a way to get the quoting right using `printf` and `%q`, but it's going to be tricky to get an arbitrary sequence of words correctly quoted into a single string to pass to `sh -c`. – chepner Sep 16 '13 at 14:01
  • OP asked for more **concise**, so why are you adding something like this, when there are already 5 more concise answers? – zoska Sep 16 '13 at 14:26
  • Once you define the definition of `rep` in your `.bashrc`, further invocations become a lot more concise. – musiphil Jan 21 '14 at 17:51
  • Beware, though, that `rep` as defined above will take up the standard input for iteration, for no good reason. Why not just use a simple `for` loop like `for ((x=$s; x<=$e; ++x)); do ...; done`? – musiphil Jan 21 '14 at 17:53
2

The script file

bash-3.2$ cat test.sh 
#!/bin/bash

echo "The argument is  arg: $1"

for ((n=0;n<$1;n++));
do
  echo "Hi"
done

and the output below

bash-3.2$  ./test.sh 3
The argument is  arg: 3
Hi
Hi
Hi
bash-3.2$
upog
  • 4,965
  • 8
  • 42
  • 81
2

A little bit naive but this is what I usually remember off the top of my head:

for i in 1 2 3; do
  some commands
done

Very similar to @joe-koberg's answer. His is better especially if you need many repetitions, just harder for me to remember other syntax because in last years I'm not using bash a lot. I mean not for scripting at least.

akostadinov
  • 17,364
  • 6
  • 77
  • 85
0

How about the alternate form of for mentioned in (bashref)Looping Constructs?

SamB
  • 9,039
  • 5
  • 49
  • 56