7

I run below script:

for (( i=1; i <= 5; i++ ))    
do
    echo "Random number $i: $RANDOM"
done

I am getting below Error:

Syntax error: Bad for loop variable

why this syntax is not working?

A.B.
  • 90,397

3 Answers3

13

As heemayl's answer points out, your construction is a bashism. The standard way of doing what you want would be:

for i in 1 2 3 4 5
do
  echo "Random number $i: $RANDOM"
done

Or, more easily expandably:

for i in $(seq 1 5)
do
  echo "Random number $i: $RANDOM"
done

(See man seq for more detail).

evilsoup
  • 4,485
6

Your original script has a (( i=1; i <= 5; i++ )) construct in for loop which is a bashism and hence is not being understood by dash.

In Ubuntu sh is a symbolic link to dash, so when you are running

sh ./script.sh

you are basically running

dash ./script.sh

As i mentioned earlier dash is not understanding the C-like for loop construct, it is showing the error.

To run the script using bash, you can:

  • Run it as as an argument to bash e.g. bash script.sh (you don't need to make the script executable)

  • Make the script executable and from a bash shell run (from the directory containing the script):

    ./script.sh 
    
  • The most portable way is to use a shebang (#!/bin/bash or preferably #!/usr/bin/env bash) as the first line of your script. In this way you can run the script from any shell by ./script.sh, the script will be interpreted as a bash script and hence will be executed accordingly.

heemayl
  • 91,753
  • For even greater portability, a bash script can start with #!/usr/bin/env bash. Although not 100% of Unix-like systems have env in /usr/bin, those that don't are rarer than those with bash not in bin. (bin doesn't have bash on most systems where bash is installed but not part of the base system, e.g., FreeBSD.) – Eliah Kagan May 10 '15 at 21:16
  • @EliahKagan Yes..i forgot to mention env..that will be most portable.. – heemayl May 10 '15 at 21:29
5

Thought I'd contribute to the discussion a little bit.

It's already been mentioned that the syntax used in your script is a bashism and hence isn't portable, even though this is C-like and is well understood by those familiar with java and C. From asking my own question on unix.stackexchange.com , I've also learned that syntax such as for i in $(seq 1 5) generates a set of numbers first and then iterates , which can be wasteful if you have a very large set.

A better way to simulate C-like behavior, which is portable, is to use while loop, with a variable that can be incremented. For instance,

#!/bin/sh
number=0
while [ "$number" -lt 10 ]
do
        printf "\t%d" "$number"
        number=`expr $number + 1 `
done

This works with bash, dash, zsh, ksh, mksh . . . or basically any shell related to bourne shell. In fact, I've a Unix System V book from like 1995, and I've tested their example for bourne shell and it still works. The behaviour is also that of C-like for loop: you have initial condition, testing condition within while [ . . .], and update condition at the end.

csh and tcsh have syntax closer to C language, but its not portable to other shells, and they're not recommended to be used in scripting.

Addition:

Concerning portability of $RANDOM, according to this page on ubuntu wiki, with dash random number generation should rely on using /dev/urandom

Sergiy Kolodyazhnyy
  • 105,154
  • 20
  • 279
  • 497
  • 1
    This is indeed much more efficient when the loop variable can take on many values. For example, on my system, using command time -f '%E' dash -c '...' as the test harness, for i in $(seq 1 100000000); do test $i -eq 1000 && break; done took 0:48.37, but i=1; while [ $i -lt 100000000 ]; do test $i -eq 1000 && break; i=$(expr $i + 1); done took 0:02.23. – Eliah Kagan May 10 '15 at 21:13