6
votes

I am trying to learn shell scripting, so I created a simple script with a loop that does nothing:

#!/bin/bash
names=(test test2 test3 test4)
for name in ${names[@]}
do
        #do something
done

however, when I run this script I get the following errors:

./test.sh: line 6: syntax error near unexpected token done'
./test.sh: line 6: done'

What have I missed here? are shell scripts 'tab sensitive'?

6
Just see once stackoverflow.com/a/42478844/6545759 It may be helpful - Mihit Gandhi

6 Answers

7
votes

No, shell scripts are not tab sensitive (unless you do something really crazy, which you are not doing in this example).

You can't have an empty while do done block, (comments don't count) Try substituting echo $name instead

#!/bin/bash
names=(test test2 test3 test4)
for name in ${names[@]}
do
       printf "%s " $name
done
printf "\n"

output

test test2 test3 test4
6
votes

dash and bash are a bit brain-dead in this case, they do not allow an empty loop so you need to add a no op command to make this run, e.g. true or :. My tests suggest the : is a bit faster, although they should be the same, not sure why:

time (i=100000; while ((i--)); do :; done)

n average takes 0.262 seconds, while:

time (i=100000; while ((i--)); do true; done)

takes 0.293 seconds. Interestingly:

time (i=100000; while ((i--)); do builtin true; done)

takes 0.356 seconds.

All measurements are an average of 30 runs.

4
votes

Bash has a built-in no-op, the colon (:), which is more lightweight than spawning another process to run true.

#!/bin/bash
names=(test test2 test3 test4)
for name in "${names[@]}"
do
    :
done

EDIT: William correctly points out that true is also a shell built-in, so take this answer as just another option FYI, not a better solution than using true.

1
votes

You could replace the nothing with 'true' instead.

1
votes

You need to have something in your loop otherwise bash complains.

1
votes

This error is expected with some versions of bash where the script was edited on Windows and so the script actually looks as follows:

#!/bin/bash^M
names=(test test2 test3 test4)^M
for name in ${names[@]}^M
do^M
       printf "%s " $name^M
done^M
printf "\n"^M

where the ^M represents the carriage-return character (0x0D). This can easily be seen in vi by using the binary option as in:

vi -b script.sh

To remove those carriage-return characters simply use the vi command:

1,$s/^M//

(note that the ^M above is a single carriage-return character, to enter it in the editor use sequence Control-V Control-M)