web-dev-qa-db-fra.com

Existe-t-il un meilleur moyen d'exécuter une commande N fois dans bash?

Je lance 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, 10 fois dans ce cas.

Souvent, some_command est en réalité une chaîne de commandes ou un pipeline.

Y a-t-il une manière plus concise de faire ceci?

261
bstpierre
for run in {1..10}
do
  command
done

Ou comme une ligne pour ceux qui veulent copier et coller facilement:

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

En utilisant une constante:

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

Utiliser une variable (peut inclure des expressions mathématiques):

x=10 for ((n=0; n < (x / 2); n++)); do some_command; done
193
BatchyX

Un autre moyen simple de le pirater:

seq 20 | xargs -Iz echo "Hi there"

courir écho 20 fois.


Notez que seq 20 | xargs -Iz echo "Hi there z" afficherait:

Salut il 1
Salut à tous
...

112
mitnk

Si vous utilisez le shell zsh:

repeat 10 { echo 'Hello' }

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

65
Wilson Silva

En utilisant GNU parallèle, 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'introduction pour une introduction rapide: https://www.youtube.com/playlist?list=PL284C9FF2488BC6D1

Parcourez le tutoriel ( http://www.gnu.org/software/parallel/parallel_tutorial.html ). Vous commandez en ligne avec vous aimer pour cela.

24
Ole Tange

Une simple fonction dans le fichier de configuration bash (~/.bashrc souvent) 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
14
steel

Une autre forme de votre exemple:

n=0; while (( n++ < 10 )); do some_command; done
11
Dennis Williamson

xargs est rapide:

#!/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 plusieurs fois la commande /usr/bin/true, 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 devez effectuer plusieurs commandes à chaque itération de la boucle, ce sera tout aussi rapide, voire plus rapide, que de passer sh -c 'command1; command2; ...' à xargs

Le -P1 pourrait également être remplacé par, par exemple, -P8 pour générer 8 processus en parallèle afin d’obtenir une nouvelle accélération importante.

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

10
James Scriven

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
firejox

D'une part, vous pouvez l'envelopper dans une fonction:

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

Appelez ça comme:

$ manytimes 3 echo "test" | tr 'e' 'E'
tEst
tEst
tEst
6
bta
for _ in {1..10}; do command; done   

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

5
Ali Omary

Si vous êtes prêt à le faire régulièrement, vous pouvez exécuter la commande suivante pour l'exécuter toutes les 1 s, indéfiniment. Vous pouvez mettre en place d'autres contrôles personnalisés pour l'exécuter n fois.

watch -n 1 some_command

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

Selon la page de manuel OSX, il y a aussi

L'option --cumulative rend la mise en surbrillance "collante", présentant un affichage continu de toutes les positions jamais modifiées. L'option -t ou --no-title désactive l'en-tête indiquant l'intervalle, la commande et l'heure actuelle en haut de l'écran, ainsi que la ligne vierge suivante.

La page de manuel Linux/Unix peut être trouvée ici

3
Pankaj Singhal

J'ai résolu avec cette boucle, où répétition est un entier représentant le nombre de boucles

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

Toutes les réponses existantes semblent nécessiter bash , et ne fonctionnent 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
cnst

Encore une autre réponse: utilisez le développement de paramètre sur des paramètres vides:

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

Testé sur Centos 7 et MacOS.

1
jcollum

Un peu naïf, mais voici ce dont je me souviens le plus souvent:

for i in 1 2 3; do
  some commands
done

Très similaire à la réponse de @ joe-koberg. C'est mieux, surtout si vous avez besoin de nombreuses répétitions, mais il est plus difficile pour moi de me souvenir d'une autre syntaxe, car ces dernières années, je n'ai pas beaucoup utilisé bash. Je veux dire pas pour le script au moins.

1
akostadinov

Le fichier 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
upog

Les boucles car 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 en tant que paramètre pour votre appel, 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 sans problème qu’avec de simples commandes (sans pipe, etc.). vous pouvez toujours utiliser un sh -c pour faire des choses plus compliquées, mais cela ne vaut pas la peine.

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

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 chiffres donnent la plage. Le @ sera traduit en numéro d'itération. Maintenant, vous pouvez aussi l'utiliser avec des pipes:

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

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

0
stacksia

Que diriez-vous de la forme alternative de for mentionnée dans (bashref) Looping Constructs ?

0
SamB