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?
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
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
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
...
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 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.
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
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 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.
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
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
for _ in {1..10}; do command; done
Notez le trait de soulignement au lieu d'utiliser une variable.
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
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
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
$
_
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.
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.
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$
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.
Que diriez-vous de la forme alternative de for
mentionnée dans (bashref) Looping Constructs ?