¿Cómo puedo crear un ciclo aritmético en un script de shell POSIX?

12

Sé cómo crear un forciclo aritmético bash.

¿Cómo se puede hacer un bucle equivalente en un script de shell POSIX?

Como hay varias formas de lograr el mismo objetivo, siéntase libre de agregar su propia respuesta y elaborar un poco sobre cómo funciona.

A continuación se muestra un ejemplo de uno de estos bashbucles:

#!/bin/bash
for (( i=1; i != 10; i++ ))
do
    echo "$i"
done
LinuxSecurityFreak
fuente
@ StéphaneChazelas porque era una nota sobre la historia que parecía estar basada en un malentendido ya que el OP no estaba sugiriendo que era una cosa de bash, sino simplemente usando bash como ejemplo. Realmente no parecía relevante.
terdon

Respuestas:

13

He encontrado información útil en el wiki de Shellcheck.net , cito:

  1. Golpetazo:

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

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

aunque tenga en cuenta que i++no es POSIX, por lo que tendría que traducirse, por ejemplo, a i += 1o i = i + 1.


Por lo tanto, la secuencia de comandos anterior en la pregunta se puede reescribir en POSIX utilizando reglas como esta:

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

Aunque aquí, puedes hacerlo más legible con:

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

como en init, estamos asignando un valor constante, por lo que no necesitamos evaluar una expresión aritmética. El i != 10in testse puede traducir fácilmente a una [expresión y next, al usar una asignación variable de shell en lugar de una asignación variable dentro de una expresión aritmética, nos permite deshacernos de :la necesidad de citar.


Además de i++-> i = i + 1, hay más traducciones de construcciones específicas de ksh / bash que no son POSIX que podría tener que hacer:

  • i=1, j=2. El ,operador aritmético no es realmente POSIX (y entra en conflicto con el separador decimal en algunas configuraciones regionales con ksh93). Podría reemplazarlo con otro operador +como en el caso, : "$(((i=1) + (j=2)))"pero usarlo i=1 j=2sería mucho más legible.
  • a[0]=1: sin matrices en shells POSIX
  • i = 2**20: sin operador de potencia en la sintaxis de shell POSIX. <<aunque es compatible con potencias de dos, uno puede usar i = 1 << 20. Para otros poderes, uno puede recurrir a bc:i=$(echo "3 ^ 20" | bc)
  • i = RANDOM % 3: no POSIX. El más cercano en el POSIX toolchest es i=$(awk 'BEGIN{srand(); print int(rand() * 3)}').
LinuxSecurityFreak
fuente
2

Gracias por el conocimiento de fondo sobre la diferencia. Una caída en el reemplazo que funcionó para mí cuando uso shellcheck.net fue la siguiente.

GOLPETAZO

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

POSIX

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

algunas personas notaron que seq también es una opción usando seq 1 10. Crear un bucle, sin embargo, esto depende de que os tenga seq.

Jimmy MG Lim
fuente
tenga en cuenta que he colocado i = 0 en la misma línea que while. aunque la legibilidad no es excelente, es un complemento directo. Esto asegura que la variable i no esté contaminada por ningún otro lugar definido en el script.
Jimmy MG Lim