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?
19 réponses
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
...
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.
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.
Une Autre forme de votre exemple:
n=0; while (( n++ < 10 )); do some_command; done
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.
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.
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
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
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
$
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.
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
Que diriez-vous de la forme alternative de for
mentionnée dans (bashref)constructions en boucle ?
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.
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$
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.