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.
  • 89,123
  • 21
  • 245
  • 323
Akshay Patel
  • 335
  • 2
  • 4
  • 7
  • 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
  • ya ..it is working fine in terminal but getting error in script file – Akshay Patel May 10 '15 at 18:17
  • 1
    What command are you using to run the script? – heemayl May 10 '15 at 18:19
  • Does your script have `#! /bin/bash` at the beginning? – muru May 10 '15 at 18:21
  • i was using sh command to execute the script so due to that script was not executing but once i use " ./abcd.sh " command,it is working fine..do you have any explination on this...? – Akshay Patel May 10 '15 at 18:21
  • 1
    As far as your previous records are concerned, you should [read this](http://stackoverflow.com/help/someone-answers) – heemayl May 10 '15 at 18:46
  • 1
    You may be interested in an answer by a user posted on my question here: http://unix.stackexchange.com/questions/198985/how-to-make-mksh-for-loop-go-from-1-to-n – Sergiy Kolodyazhnyy May 10 '15 at 19:49

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,435
  • 1
  • 19
  • 26
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
  • 90,425
  • 20
  • 200
  • 267
  • 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
  • 103,293
  • 19
  • 273
  • 492
  • 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