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

Je lance occasionnellement 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 plusieurs fois de suite – 10 fois dans ce cas.

some_command est souvent une chaîne de commandes ou un pipeline.

Existe-t-il un moyen plus concis de le faire?

 for run in {1..10} do command done 
 for ((n=0;n<10;n++)); do some_command; done 

Un autre moyen simple de le pirater:

 seq 20 | xargs -Iz echo "Hi there" 

exécuter echo 20 fois.


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

Salut là 1
Salut là 2

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.

En utilisant GNU Parallel, vous pouvez faire:

 parallel some_command ::: {1..1000} 

Si vous ne voulez pas que le nombre soit un argument et ne lancez 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 amour pour vous.

Une autre forme de votre exemple:

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

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 avez besoin de faire plusieurs commandes dans chaque itération de la boucle, ce sera aussi rapide, ou peut-être plus rapide, que de passer commande sh -c 'command1; command2; ...' sh -c 'command1; command2; ...' sh -c 'command1; command2; ...' à xargs

Le -P1 pourrait aussi être changé pour, par exemple, -P8 pour engendrer 8 processus en parallèle pour obtenir une autre grande amélioration de la vitesse.

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

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 

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 
 for _ in {1..10}; do command; done 

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

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 

Si vous le faites régulièrement, vous pouvez exécuter la commande suivante pour l’exécuter indéfiniment. Vous pouvez mettre en place d’autres contrôles personnalisés pour l’exécuter plusieurs 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 évidence “collante”, présentant un affichage courant de toutes les positions qui ont déjà changé. 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 vide suivante.

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

Toutes les réponses existantes semblent nécessiter bash , et ne fonctionnent pas avec un BSD UNIX /bin/sh standard (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 $ 

J’ai résolu avec cette boucle, où la répétition est un entier qui représente le numéro de la boucle

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

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

Car 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 appel, utilisez:

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

Edit: Il a été justement commenté que la solution donnée ci-dessus ne fonctionnerait bien qu’avec de simples exécutions de commandes (pas de pipes, etc.). vous pouvez toujours utiliser un sh -c pour faire des choses plus compliquées, mais ça ne vaut pas la peine.

Une autre méthode que j’utilise est généralement 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 au numéro d’itération. Maintenant, vous pouvez aussi l’utiliser avec des tuyaux:

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

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

Encore une autre réponse: Utilisez l’extension de paramètre sur les parameters vides:

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

Testé sur Centos 7 et MacOS.

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$ 

Un peu naïf mais c’est ce dont je me souviens d’habitude:

 for i in 1 2 3; do some commands done 

Très similaire à la réponse de @joe-koberg. Son mieux, surtout si vous avez besoin de beaucoup de répétitions, juste plus difficile pour moi de me souvenir d’autres syntaxes car ces dernières années, je n’utilise pas beaucoup de bash . Je veux dire pas pour le script au moins.