Comment puis-je créer une boucle arithmétique dans un script shell POSIX?

12

Je sais comment créer une forboucle arithmétique bash.

Comment faire une boucle équivalente dans un script shell POSIX?

Comme il existe différentes façons d'atteindre le même objectif, n'hésitez pas à ajouter votre propre réponse et à expliquer un peu comment cela fonctionne.

Voici un exemple d'une telle bashboucle:

#!/bin/bash
for (( i=1; i != 10; i++ ))
do
    echo "$i"
done
LinuxSecurityFreak
la source
@ StéphaneChazelas parce que c'était une note sur l'histoire qui semblait être basée sur un malentendu puisque le PO ne suggérait pas que c'était une chose bash, mais utilisait simplement bash comme exemple. Cela ne semblait pas vraiment pertinent.
terdon

Réponses:

13

J'ai trouvé des informations utiles dans le wiki Shellcheck.net , je cite:

  1. Frapper:

    for ((init; test; next)); do foo; done
  2. POSIX:

    : "$((init))"
    while [ "$((test))" -ne 0 ]; do foo; : "$((next))"; done

mais méfiez-vous qui i++n'est pas POSIX devrait donc être traduit, par exemple en i += 1ou i = i + 1.


Ainsi, le script ci-dessus dans la question peut être réécrit au niveau POSIX en utilisant ces règles comme ceci:

#!/bin/sh
: "$((i=1))"
while [ "$((i != 0))" -ne 0 ]
do
    echo "$i"
    : "$((i = i + 1))"
done

Bien qu'ici, vous pouvez le rendre plus lisible avec:

#!/bin/sh
i=1
while [ "$i" -ne 10 ]
do
    echo "$i"
    i=$((i + 1))
done

comme dans init, nous attribuons une valeur constante, nous n'avons donc pas besoin d'évaluer une expression arithmétique. Le i != 10in testpeut facilement être traduit en une [expression, et pour next, en utilisant une affectation de variable shell par opposition à une affectation de variable à l'intérieur d'une expression arithmétique, nous permet de nous débarrasser de :la nécessité de citer.


À côté de i++-> i = i + 1, il existe d'autres traductions de constructions spécifiques à ksh / bash qui ne sont pas POSIX que vous pourriez avoir à faire:

  • i=1, j=2. L' ,opérateur arithmétique n'est pas vraiment POSIX (et entre en conflit avec le séparateur décimal dans certains paramètres régionaux avec ksh93). Vous pouvez le remplacer par un autre opérateur comme +dans, : "$(((i=1) + (j=2)))"mais son utilisation i=1 j=2serait beaucoup plus lisible.
  • a[0]=1: pas de tableaux dans les shells POSIX
  • i = 2**20: aucun opérateur de puissance dans la syntaxe shell POSIX. <<est pris en charge, donc pour des puissances de deux, on peut utiliser i = 1 << 20. Pour les autres pouvoirs, on peut recourir à bc:i=$(echo "3 ^ 20" | bc)
  • i = RANDOM % 3: pas POSIX. Le plus proche dans le toolchest POSIX est i=$(awk 'BEGIN{srand(); print int(rand() * 3)}').
LinuxSecurityFreak
la source
2

merci pour votre connaissance approfondie de la différence. Une baisse de remplacement qui fonctionne pour moi lors de l'utilisation de shellcheck.net était comme ci-dessous.

FRAPPER

for i in {1..100}; do  
  ...  
done  

POSIX

i=0; while [ $i -le 100 ]; do  
  ...  
  i=$(( i + 1 ))  
done

certaines personnes ont noté que seq est également une option en utilisant seq 1 10. Créer une boucle, mais cela dépend du fait que os a seq.

Jimmy MG Lim
la source
notez que j'ai placé i = 0 sur la même ligne que while. bien que la lisibilité ne soit pas excellente, il s'agit d'un drop-in à une ligne. Cela garantit que la variable i n'est pas entachée par un autre endroit défini dans le script.
Jimmy MG Lim