Syntaxe de la boucle «for» du script shell


191

J'ai fait fonctionner les éléments suivants:

for i in {2..10}
do
    echo "output: $i"
done

Elle produit un tas de lignes output: 2, output: 3, ainsi de suite.

Cependant, essayez d'exécuter ce qui suit:

max=10
for i in {2..$max}
do
    echo "$i"
done

produit ce qui suit:

output: {2..10}

Comment puis-je faire en sorte que le compilateur réalise qu'il doit traiter $ max comme l'autre extrémité du tableau, et non comme faisant partie d'une chaîne?


2
quel système et quel shell utilisez-vous? Quel genre de système maladroit a sh ou bash, mais n'a pas de seq, un coreutil?
whatsisname

11
FreeBSD ne le fait pas.
Nietzche-jou

echo "$idevrait être echo "$i"- ne résoudra pas le problème, cependant.
Dave Jarvis

Petit style nit: Je vois généralement les mots do- thenclés et sur la même ligne que foret if, respectivement. Par exemple,for i in {2..10}; do
un nerd payé le

Réponses:


234

L'expansion d' accolades , {x..y} est effectuée avant les autres expansions, vous ne pouvez donc pas l'utiliser pour des séquences de longueur variable.

Utilisez plutôt la seq 2 $maxméthode indiquée par l' utilisateur mob .

Donc, pour votre exemple, ce serait:

max=10
for i in `seq 2 $max`
do
    echo "$i"
done

Il n'y a aucune bonne raison d'utiliser une commande externe telle que seqcompter et incrémenter des nombres dans la boucle for, il est donc recommandé d'éviter d'utiliser seq. Cette commande est laissée pour compatibilité avec l'ancien bash. Les commandes intégrées sont assez rapides. for (( EXP1; EXP2; EXP3 )) ...
miller

13
@miller, la for (( ... ))syntaxe n'est pas POSIX et cela signifie par exemple que cela ne fonctionnera pas directement sur des choses comme Alpine Linux. seqEst-ce que.
Wernight

@Wernight Pas seulement Alpine Linux; #!/bin/shest Dash dans Debian / Ubuntu.
Franklin Yu

72

Essayez la version d'expression arithmétique de for:

max=10
for (( i=2; i <= $max; ++i ))
do
    echo "$i"
done

Ceci est disponible dans la plupart des versions de bash et devrait également être compatible avec Bourne shell (sh).


1
@Flow: Hm, je viens de l'essayer sur quelques systèmes (Linux et BSD) avec #! / Bin / sh et cela a bien fonctionné. Invoqué sous bash et spécifiquement sous / bin / sh, fonctionnait toujours. Peut-être que la version de sh compte? Êtes-vous sur un vieil Unix?
système PAUSE

8
Très peu de systèmes ont un système dédié sh, ce qui en fait un lien vers un autre shell. Idéalement, un shell invoqué nesh prendrait en charge que ces fonctionnalités dans le standard POSIX, mais laisserait par défaut certaines de leurs fonctionnalités supplémentaires passer. La boucle for de style C n'est pas une fonctionnalité POSIX, mais peut être en mode par le shell réel. sh
chepner le

27

Étape de la boucle manuellement:

i = 0
max = 10
tandis que [$ i -lt $ max]
faire
    echo "sortie: $ i"
    vrai $ ((i ++))
terminé

Si vous ne devez pas être totalement POSIX, vous pouvez utiliser l'arithmétique pour la boucle:

max = 10
pour ((i = 0; i <max; i ++)); faire echo "sortie: $ i"; terminé

Ou utilisez jot (1) sur les systèmes BSD:

pour i dans $ (jot 0 10); faire echo "sortie: $ i"; terminé

3
true $(( i++ ))ne fonctionne pas dans tous les cas, donc la plupart des portables le seraient true $((i=i+1)).
Débit

le point-virgule ne doit pas entrer "for ((i = 0; i <max; i ++));"
logan

9

Il y a plus d'une façon de le faire.

max=10
for i in `eval "echo {2..$max}"`
do
    echo "$i"
done

7
Un risque de sécurité, car evalévaluera tout ce que vous définissez max. Considérez max="2}; echo ha; #", puis remplacez echo hapar quelque chose de plus destructeur.
chepner le

6
(S) il a mis max à 10. Aucun risque.
Conrad Meyer

9

Si la seqcommande disponible sur votre système:

for i in `seq 2 $max`
do
  echo "output: $i"
done

Sinon, utilisez le pauvre homme seqavec perl:

seq=`perl -e "\$,=' ';print 2..$max"`
for i in $seq
do
  echo "output: $i"
done

Regardez ces guillemets.


Je viens de vérifier cela; il ne semble pas que ce soit le cas.
eykanal

5

C'est un moyen:
Bash:

max=10
for i in $(bash -c "echo {2..${max}}"); do echo $i; done

La méthode Bash ci-dessus fonctionnera pour kshet zshaussi, quand bash -cest remplacé par ksh -cou zsh -crespectivement.

Remarque: for i in {2..${max}}; do echo $i; donefonctionne dans zshet ksh.


4

Nous pouvons itérer une boucle comme la programmation en C.

#!/bin/bash
for ((i=1; i<=20; i=i+1))
do 
      echo $i
done

2

Ici, cela a fonctionné sur Mac OS X.

Il comprend l'exemple d'une date BSD, comment incrémenter et décrémenter la date également:

for ((i=28; i>=6 ; i--));
do
    dat=`date -v-${i}d -j "+%Y%m%d"` 
    echo $dat
done

2

Eh bien, comme je n'avais pas la seqcommande installée sur mon système (Mac OS X v10.6.1 (Snow Leopard)), j'ai fini par utiliser une whileboucle à la place:

max=5
i=1

while [ $max -gt $i ]
do
    (stuff)
done

* Shrugs * Tout ce qui fonctionne.


2
seq est relativement nouveau. Je l'ai découvert il y a seulement quelques mois. Mais vous pouvez utiliser une boucle «for» !! L'inconvénient d'un «while» est que vous devez vous rappeler d'incrémenter le compteur quelque part dans la boucle, ou bien de faire une boucle vers le bas.
PAUSE du système

1

Tout cela fait {1..8}et devrait être POSIX. Ils ne casseront pas non plus si vous mettez un conditionnel continuedans la boucle. La voie canonique:

f=
while [ $((f+=1)) -le 8 ]
do
  echo $f
done

Autrement:

g=
while
  g=${g}1
  [ ${#g} -le 8 ]
do
  echo ${#g}
done

et un autre:

set --
while
  set $* .
  [ ${#} -le 8 ]
do
  echo ${#}
done

1

Utilisation:

max=10
for i in `eval echo {2..$max}`
do
    echo $i
done

Vous avez besoin de l'appel explicite 'eval' pour réévaluer {} après la substitution de variable.

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.