user3624843
user3624843

Reputation: 175

How do I rewrite a bash 'while' loop as a 'for' loop?

This is my bash scripting code so I want to know How to Rewrite the below Bash script using a “for” loop instead of the “while” loop.

  #!/bin/bash
    if [ $# -gt 0 ]; then                
           a=0;                
           if [ -f RandNos ]; then
               rm RandNos;
           fi

           while [ $a -lt $1 ]
           do
               a='expr $a + 1';
               myrand=$RANDOM;

              if [ "$2" "1"]; then
                  echo "No. $a ==> $myrand";
              fi

              echo $myrand>>RandNos
           done                
    else 
        echo "please use with an argument..."
    fi

Thanks.

Upvotes: 0

Views: 391

Answers (5)

LGkash
LGkash

Reputation: 21

!/bin/bash

if [ $# -gt 0 ]; then

a=0;
    if [ -f RandNos ]; then
        rm RandNos;
    fi
    for (( i=$a; i<$1; i++ ))

    do
   
    myrand=$RANDOM;
    if [ "$2" = "1" ]; then
        echo "No. $a ==> $myrand";
    fi
    echo $myrand >> RandNos
    done
else
    echo "please use with an argument..."
fi

Upvotes: 0

willeM_ Van Onsem
willeM_ Van Onsem

Reputation: 476544

Simply rewriting it with a for loop results in:

#!/bin/bash
if [ $# -gt 0 ]; then
   if [ -f RandNos ]; then
      rm RandNos;
   fi
   lim=$(expr $1 - 1)
   as=$(seq 0 $lim)
   for a in $as
   do
      a='expr $a + 1';
      myrand=$RANDOM;
      if [ "$2" "1"]; then  # <- Caveat: conditional is BROKEN
         echo "No. $a ==> $myrand";
      fi
      echo $myrand>>RandNos
   done
else 
   echo "please use with an argument..."
fi

But there are several things wrong with the script anyhow. Like the last if statement.

Upvotes: 1

mklement0
mklement0

Reputation: 437208

The short of it: for counter-based loops, use the C-like form of the for loop:

    for (( a = 0; a < $1; a++ )); do
        # ... use $a
    done

(This replaces while [ $a -lt $1 ]; do a='expr $a + 1' ...; done.)

See below for more on the rules that apply inside (( ... )).


As for the rest of your code:

  • Conditional [ "$2" "1"] is broken: it's missing the mandatory space before ]
    • With that fixed, it'll only work if $2 expands to a unary test operator such as -n.
    • Perhaps you meant if [[ -z $myrand ]]; then, to check if $RANDOM resulted in a nonempty string?
  • a='expr $a + 1' - which you don't need anymore with the for loop - doesn't actually invoke expr, because you're using single quotes - you'd need backticks (`) instead, or, preferably, the modern equivalent: $(expr $a + 1). However, with arithmetic evaluation, this could be simplified to (( ++a )).

  • [ ... ] conditionals work in bash, but they're provided for POSIX compatibility - use [[ ... ]] as the bash-specific alternative, which is more robust, has more features, and is faster.

  • bash statements only need terminating with ; if you place multiple on a single line

    • Note that bash considers do ... and then ... separate statements, hence you often see if ...; then and for ...; do.
  • In general, I encourage you to syntax-check your shell code at http://shellcheck.net - it's a great tool for detecting syntax problems.

Note how different rules apply inside (( ... )) compared to elsewhere in bash:

  • spaces around the = in the variable assignment are allowed.
  • referencing a variable without the $ prefix (a++) is allowed.
  • < performs numerical comparison (whereas inside [[ ... ]] it's lexical) -i.e., it's the more natural equivalent to -lt inside [ ... ] or [[ ... ]].
  • several other mathematical and even bit-wise operators are supported
  • ...

All these different rules apply when bash operates in an arithmetic context, which applies to (( ... )), $(( ... )), array subscripts, and other cases.
For all the rules, run man bash and read the ARITHMETIC EVALUATION section.

Upvotes: 4

Deleted User
Deleted User

Reputation: 2541

Several things can be improved:

#!/bin/bash
if (( $# )); then                              # anything but 0 is true
   rm -f RandNos                            # remove if existing, otherwise fail silently
   for ((a=0; a<$1; a++)); do
      myrand=$RANDOM

#  what is the intention here?
      (( $2 > 1 )) && echo "No. $a ==> $myrand"
      echo "$myrand" >> RandNos
   done
else
   echo "please use with an argument..."
fi

not sure what your intention was with the [ "$2" "1" ] expression. it is probably not what I made from it.

for ((a=1; a<=$1; a++)); do

may reflect your intended logic better, as you use $a for output only after incrementing it. as pointed out and corrected by @mklement0

Upvotes: 1

spinus
spinus

Reputation: 5765

if [ $# -lt 1 ];then
    echo "First argument must be number".
    exit 1;
fi

for a in `seq $1`
do
     ...
done

Upvotes: 1

Related Questions