Como posso criar um loop aritmético em um script de shell POSIX?

12

Eu sei como criar um forloop aritmético bash.

Como se pode fazer um loop equivalente em um script de shell POSIX?

Como existem várias maneiras de alcançar o mesmo objetivo, sinta-se à vontade para adicionar sua própria resposta e elaborar um pouco sobre como ele funciona.

Um exemplo de um desses bashloop é apresentado a seguir:

#!/bin/bash
for (( i=1; i != 10; i++ ))
do
    echo "$i"
done
LinuxSecurityFreak
fonte
@ StéphaneChazelas porque era uma nota sobre a história que parecia basear-se em um mal-entendido, já que o OP não estava sugerindo que era uma coisa do bash, mas simplesmente usando o bash como exemplo. Realmente não parecia relevante.
terdon

Respostas:

13

Encontrei informações úteis no wiki Shellcheck.net , cito:

  1. Bater:

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

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

embora tenha cuidado com o que i++não é POSIX, teria de ser traduzido, por exemplo, para i += 1ou i = i + 1.


Portanto, o script acima na pergunta pode ser reescrito no POSIX usando regras como esta:

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

Embora aqui, você pode torná-lo mais legível com:

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

como initestamos atribuindo um valor constante, não precisamos avaliar uma expressão aritmética. O i != 10in testpode ser facilmente traduzido para uma [expressão e next, usando uma atribuição de variável do shell em oposição a uma atribuição de variável dentro de uma expressão aritmética, nos livramos da :necessidade de citar.


Ao lado de i++-> i = i + 1, há mais traduções de construções específicas do ksh / bash que não são POSIX que você pode precisar fazer:

  • i=1, j=2. O ,operador aritmético não é realmente POSIX (e entra em conflito com o separador decimal em algumas localidades com ksh93). Você poderia substituí-lo por outro operador, como +no exemplo, : "$(((i=1) + (j=2)))"mas o uso i=1 j=2seria muito mais legível.
  • a[0]=1: sem matrizes em shells POSIX
  • i = 2**20: nenhum operador de energia na sintaxe do shell POSIX. <<é suportado, porém, para potências de dois, pode-se usar i = 1 << 20. Para outros poderes, pode-se recorrer a bc:i=$(echo "3 ^ 20" | bc)
  • i = RANDOM % 3: não POSIX. O mais próximo no baú da ferramenta POSIX é i=$(awk 'BEGIN{srand(); print int(rand() * 3)}').
LinuxSecurityFreak
fonte
2

obrigado pelo conhecimento aprofundado acima sobre a diferença. Uma queda na substituição que funcionou para mim ao usar o shellcheck.net foi a seguinte.

BATER

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

POSIX

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

algumas pessoas notaram que a seq também é uma opção usando a seq 1 10. Criando um loop, no entanto, isso depende de que os tenha seq.

Jimmy MG Lim
fonte
note que eu coloquei i = 0 na mesma linha que enquanto. embora a legibilidade não seja ótima, é uma apresentação única. Isso garante que a variável i não seja contaminada por nenhum outro local definido no script.
Jimmy MG Lim