6

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?

8
  • When I copy-paste this into my terminal, it works as inteded. Do you have something else stored in i or have a file named "i" in your WD? – s3lph May 10 '15 at 18:15
  • 1
    You are probably running it in sh ..the syntax is correct for bash ..how are you running it? – heemayl May 10 '15 at 18:15
  • 1
    What command are you using to run the script? – heemayl May 10 '15 at 18:19
  • 1
    As far as your previous records are concerned, you should read this – heemayl May 10 '15 at 18:46
  • 1
    You may be interested in an answer by a user posted on my question here: unix.stackexchange.com/questions/198985/… – Sergiy Kolodyazhnyy May 10 '15 at 19:49
12

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).

6
  • Brace expansion is also a bashism. – muru May 10 '15 at 18:38
  • 1
    for i in 1 2 3 4 5 is kind of trolling....use seq 1 5.. – heemayl May 10 '15 at 18:41
  • @heemayl -- Not trolling, I just had to double-check the seq syntax before I included it – evilsoup May 10 '15 at 18:45
  • @heemayl no trolling, some users don't know all the minute details of shell scripting =) – Sergiy Kolodyazhnyy May 10 '15 at 19:48
  • 3
    Not that one can say seq is all that standard either. It's not POSIX. – muru May 10 '15 at 19:49
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.

2
  • 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

1
  • 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

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