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?

Eykanal
la source
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
comment s'appelle-t-il
la source
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).

PAUSE système
la source
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é
Nietzche-jou
la source
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
éphémère
la source
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.

foule
la source
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.

Jahid
la source
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
éruptions cutanées
la source
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
minhas23
la source
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.

Eykanal
la source
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

la source
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.

Chris Dodd
la source