Est-il une meilleure façon d'exécuter une commande N fois en bash?

J'exécute parfois une ligne de commande bash comme ceci:

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

Pour exécuter some_command un certain nombre de fois de suite-10 fois dans ce cas.

Souvent some_command est vraiment une chaîne de commandes ou d'un pipeline.

Y a-t-il un moyen plus concis de le faire?

220
demandé sur jww 2010-09-17 21:56:25

19 réponses

for run in {1..10}
do
  command
done
305
répondu Joe Koberg 2010-09-17 18:01:50
for ((n=0;n<10;n++)); do some_command; done
171
répondu BatchyX 2010-09-17 18:01:27

Un Autre moyen simple de le pirater:

seq 20 | xargs -Iz echo "Hi there"

Exécutez echo 20 fois.


Notez que {[1] } produirait:

Bonjour 1
Bonjour 2
...

98
répondu mitnk 2015-10-27 22:21:57

Si vous utilisez le shell zsh:

repeat 10 { echo 'Hello' }

Où 10 est le nombre de fois où la commande sera répétée.

48
répondu Wilson Silva 2016-02-29 12:42:20

En utilisant GNU Parallel, vous pouvez faire:

parallel some_command ::: {1..1000}

Si vous ne voulez pas le nombre comme argument et n'exécutez qu'un seul travail à la fois:

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

Regardez la vidéo d'intro pour une introduction rapide: https://www.youtube.com/playlist?list=PL284C9FF2488BC6D1

Parcourez le tutoriel ( http://www.gnu.org/software/parallel/parallel_tutorial.html ). Vous ligne de commande avec amour vous pour elle.

21
répondu Ole Tange 2014-01-17 22:20:20

Une Autre forme de votre exemple:

n=0; while (( n++ < 10 )); do some_command; done
10
répondu Dennis Williamson 2010-09-17 19:06:28

xargs est rapidement:

#!/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}

Sur un Linux 64 bits moderne, donne:

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

Cela a du sens, car la commande xargs est un processus natif unique qui génère la commande /usr/bin/true plusieurs fois, au lieu des boucles for et while qui sont toutes interprétées dans Bash. Bien sûr, cela ne fonctionne que pour une seule commande; si vous avez besoin de faire plusieurs commandes à chaque itération de la boucle, elle sera tout aussi rapide, ou peut-être plus rapide, que de passer sh -c 'command1; command2; ...' à xargs

Le -P1 pourrait également être changé en, disons, -P8 pour générer 8 processus en parallèle pour obtenir un autre gros coup de pouce en vitesse.

Je ne sais pas pourquoi GNU parallel est si lent. J'aurais pensé que ce serait comparable à xargs.

9
répondu James Scriven 2015-04-05 18:38:42

UNE FONCTION simple dans le fichier de configuration bash (.bashrc pour moi, sur un Mac) pourrait bien fonctionner.

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

Appelez ça comme ça.

$ runx 3 echo 'Hello world'
Hello world
Hello world
Hello world
9
répondu steel 2018-01-29 14:52:30

Xargs et seq aideront

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

La vue :

abon@abon:~$ __run_times 3  echo hello world
hello world
hello world
hello world
7
répondu firejox 2012-01-26 16:21:02
for _ in {1..10}; do command; done   

Notez le trait de soulignement au lieu d'utiliser une variable.

5
répondu Ali Omary 2014-11-12 23:04:11

Pour un, vous pouvez l'envelopper dans une fonction:

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

Appelez-le comme:

$ manytimes 3 echo "test" | tr 'e' 'E'
tEst
tEst
tEst
4
répondu bta 2010-09-17 18:06:27

Si vous êtes OK le faire périodiquement, vous pouvez exécuter la commande suivante pour l'exécuter toutes les 1 secondes indéfiniment. Vous pouvez mettre d'autres contrôles personnalisés en place pour l'exécuter n nombre de fois.

watch -n 1 some_command

Si vous souhaitez avoir une confirmation visuelle des modifications, ajouter --differences avant le ls commande.

Selon la page de manuel OSX, il y a aussi

L'option -- cumulative rend la mise en surbrillance "collante", présentant un affichage en cours d'exécution de toutes les positions qui ont jamais changé. L'-t ou --no-titre de l'option désactive l'en-tête indiquant l'intervalle, commande, et l'heure actuelle en haut de l'écran, ainsi que le ligne blanche suivante.

La page de manuel Linux/Unix se trouve ici

2
répondu Pankaj Singhal 2016-11-07 08:42:40

Toutes les réponses existantes semblent exiger bash, et ne fonctionne pas avec un BSD UNIX standard /bin/sh (par exemple, ksh sur OpenBSD).

Le code ci-dessous devrait fonctionner sur N'importe quel 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
$
1
répondu cnst 2016-12-23 04:54:55

Encore une autre réponse: Utilisez l'expansion des paramètres sur les paramètres vides:

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

Testé sur Centos 7 et MacOS.

1
répondu jcollum 2017-01-17 22:21:48

J'ai résolu avec cette boucle, où repeat est un entier qui représente le nombre de boucles

repeat=10
for n in $(seq $repeat); 
    do
        command1
        command2
    done
1
répondu fvlgnn 2017-05-19 11:03:17

Que diriez-vous de la forme alternative de for mentionnée dans (bashref)constructions en boucle ?

0
répondu SamB 2010-09-17 18:02:34

Pour les boucles sont probablement la bonne façon de le faire, Mais voici une alternative amusante:

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

Si vous avez besoin du numéro d'itération comme paramètre pour votre invocation, utilisez:

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

Edit: Il a été commenté à juste titre que la solution donnée ci-dessus ne fonctionnerait correctement qu'avec de simples exécutions de commandes (Pas de tuyaux, etc.). vous pouvez toujours utiliser un sh -c pour faire des choses plus compliquées, mais pas la peine.

Une autre méthode que j'utilise généralement est la suivante fonction:

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

Maintenant, vous pouvez l'appeler comme:

rep 3 10 echo iteration @

Les deux premiers nombres donnent la plage. Le @ sera traduit en Numéro d'itération. Maintenant, vous pouvez l'utiliser avec des tuyaux aussi:

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

Avec vous donner le nombre de fichiers dans les répertoires R1 .. R10.

0
répondu stacksia 2013-09-16 13:51:31

Le fichier de script

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

echo "The argument is  arg: $1"

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

Et la sortie ci-dessous

bash-3.2$  ./test.sh 3
The argument is  arg: 3
Hi
Hi
Hi
bash-3.2$
0
répondu upog 2017-02-10 16:45:10

Un peu naïf mais c'est ce dont je me souviens habituellement du haut de ma tête:

for i in 1 2 3; do
  some commands
done

Très similaire à la réponse de @joe-Koberg. Le sien est meilleur surtout si vous avez besoin de nombreuses répétitions, juste plus difficile pour moi de me souvenir d'une autre syntaxe car ces dernières années, je n'utilise pas beaucoup bash. Je veux dire pas pour les scripts au moins.

0
répondu akostadinov 2017-07-06 13:43:36