9

I know how to create an arithmetic for loop in bash.

How can one do an equivalent loop in a POSIX shell script?

As there are various ways of achieving the same goal, feel free to add your own answer and elaborate a little on how it works.

An example of one such bash loop follows:

#!/bin/bash
for (( i=1; i != 10; i++ ))
do
    echo "$i"
done
  • @StéphaneChazelas because it was a note on the history which seemed to be based on a misunderstanding since the OP wasn't suggesting that it was a bash thing, but simply using bash as an example. It didn't really seem relevant. – terdon Dec 13 '17 at 13:48
11

I have found useful information in Shellcheck.net wiki, I quote:

  1. Bash:

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

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

though beware that i++ is not POSIX so would have to be translated, for instance to i += 1 or i = i + 1.


So the above script in the question can be POSIX-wise re-written using those rules like this:

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

Though here, you can make it more legible with:

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

as in init, we're assigning a constant value, so we don't need to evaluate an arithmetic expression. The i != 10 in test can easily be translated to a [ expression, and for next, using a shell variable assignment as opposed to a variable assignment inside an arithmetic expression, lets us get rid of : and the need for quoting.


Beside i++ -> i = i + 1, there are more translations of ksh/bash-specific constructs that are not POSIX that you might have to do:

  • i=1, j=2. The , arithmetic operator is not really POSIX (and conflicts with the decimal separator in some locales with ksh93). You could replace it with another operator like + as in : "$(((i=1) + (j=2)))" but using i=1 j=2 would be a lot more legible.
  • a[0]=1: no arrays in POSIX shells
  • i = 2**20: no power operator in POSIX shell syntax. << is supported though so for powers of two, one can use i = 1 << 20. For other powers, one can resort to bc: i=$(echo "3 ^ 20" | bc)
  • i = RANDOM % 3: not POSIX. The closest in the POSIX toolchest is i=$(awk 'BEGIN{srand(); print int(rand() * 3)}').
1

thanks for above indepth background knowledge on the difference. A drop in replacement that work for me when using shellcheck.net was as below.

BASH

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

POSIX

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

some people noted that seq is also an option using seq 1 10 . Creating a loop, however this is dependant that os has seq.

  • note that i have placed i=0 in the same line as while. although readability is not great, it is a one liner drop-in. This ensures that variable i is not tainted by anywhere else defined in the script. – Jimmy M.G. Lim May 19 '18 at 22:49

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.