2

Here's an example simple shell script:

#!/usr/bin/env bash
if [ 1 == 1 ]; then
echo "Something"
fi

When I run this

sh ./test.sh

I get:

./test.sh: 4: ./test.sh: Syntax error: "fi" unexpected (expecting "then")

It doesn't seem to recognize the 'then' I have in there. Any clue as to why? This is Ubuntu for Windows in case that part matters.

Running shell scripts in general is not a problem, it just seems to struggle with if/else.

Zanna
  • 72,312

2 Answers2

6

There are two problems here. First, while you have a shebang line calling bash, you are executing the script as sh script.sh. The shebang line already specifies an interpreter for this script. So, just make the script executable:

chmod a+x ./test.sh

And then run it:

./test.sh

Alternatively, run it with bash explicitly:

bash ./test.sh

That will make it run with bash instead of sh which is a more limited shell and doesn't understand ==. Which brings us to the second problem and why this fails. Strangely enough, you want eq or =, not ==. The operator that tests numerical equality in POSIX shell scripts (sh is a POSIX-compliant shell) is -eq while the one that tests string equality is = and == isn't a thing.

So, if you want your script to be run by sh, change it to:

if [ 1 = 1 ]; then
    echo "Something"
fi

Which checks that the string 1 is the same as the string 1, or to compare numerically, use:

if [ 1 -eq 1 ]; then
    echo "Something"
fi

In addition, you have another problem. I can only reproduce the specific error you get in dash if I add a \r after the then. This is a classic problem when moving between Windows and Linux. While Linux uses \n as the line end character, Windows ends its lines with \r\n. This \r character is not visible to the user, but it is there and confuses the script. So, you can remove it with:

sed -i 's/\r//' ./test.sh

And in the future, use a proper text editor to write your scripts. Either one that works in the WSL system, or one that can at least be configured to not add \r to the ends of its lines.

terdon
  • 104,119
2

Let's keep it simple:

  • sh is not bash on Ubuntu (see this for reference).

  • [ is a command, same as test command. It doesn't support == for arithmetic comparison. Use -eq instead. This is one of utilities specified by POSIX standards, hence works in both bash and dash.

    if [ 1 -eq 1 ];
    then
        echo "it works!"
    fi
    
  • bash has (( which is called arithmetic expansion. This supports ==. This feature is apparently borrowed from ksh shell.

    $ var=25 bash -c 'if((var==5)); then echo Y;else echo "N";fi'      
    N
    $ var=25 bash -c 'if((var==25)); then echo Y;else echo "N";fi' 
    Y
    
  • Despite the fact that dash doesn't support if (()), in either shell $(( will work because it is mandated by POSIX standard. Taking only features that are common to both shells, we could do something like this while still using == for comparisons:

    
    $ var=25 dash -c 'if [ $((var==25)) -eq 1 ];then echo Y; else echo N;fi'
    Y
    $ var=25 dash -c 'if [ $((var==5)) -eq 1 ];then echo Y; else echo N;fi' 
    N
    
  • We could throw away if statement altogether and use case instead(note that return status conforms to C, not shell behavior, i.e. true is 1 and false is 0, not the other way around as in shell):

    $ dash -c 'case $((1==1)) in 1) echo "equal";; 0) echo "not equal";;esac'                                                                                                
    equal
    $ dash -c 'case $((1==2)) in 1) echo "equal";; 0) echo "not equal";;esac'                                                                                                
    not equal
    
  • or we can get sneaky with (ab)using arithmetic expansion:

    $ var=25 dash -c '_0(){ false;}; _1(){ true;}; if "_$((var==25))" ; then echo Y; else echo N; fi'