239

My code:

    #!/bin/sh
    #filename:choose.sh
    read choose
    [ "$choose" == "y" -o "$choose" == "Y" ] && echo "Yes" && exit 0
    [ "$choose" == "n" -o "$choose" == "N" ] && echo "No"  && exit 0
    echo "Wrong Input" && exit 0

But when I execute

    sh ./choose.sh

terminal prompt me that

   [: 4: n: :Unexpected operator
   [: 5: n: :Unexpected operator

Is there any mistake in my bash script? Thanks!

kit.yang
  • 2,758
  • 3
  • 17
  • 17
  • When i executed the same code in Linux and in cygwin i was not getting any errors – Raghuram Aug 05 '10 at 01:11
  • 2
    Cygwin has most likely aliased `sh` to `bash`. Some distributions don't offer a true `sh` anymore. Although some will argue (and I tend to agree) that if you're writing a script to be portable, write it in `sh` instead of `bash`. – Wolph Aug 05 '10 at 01:27
  • My issue was that I needed to `source foobar.sh` not `./foobar.sh` – jsta Aug 30 '16 at 16:36
  • 5
    Two mistakes: 1. use "=" not "==" for /bin/sh 2. doesn't handle the empty string. Do ${choose}BLAH == yBLAH to fix that. So also this is technically not a duplicate question. – personal_cloud Sep 21 '17 at 17:38
  • @personal_cloud, please don't recommend the `${choose}BLAH` approach -- it's much better to just quote, with `"$choose"`; constant prefixes/suffixes haven't been needed since the 1970s (as long as features marked obsolescent in the current POSIX `test` standard, like `-a` or `-o`, are avoided). – Charles Duffy Oct 31 '19 at 16:04
  • in my case variable was empty – Tsagana Nokhaeva Oct 04 '21 at 09:20

7 Answers7

403

There is no mistake in your bash script. But you are executing it with sh which has a less extensive syntax

So, you'll need run bash ./choose.sh instead, or convert the script to use POSIX compliant sh commands only, such as = between strings instead of ==.

OneCricketeer
  • 179,855
  • 19
  • 132
  • 245
Wolph
  • 78,177
  • 11
  • 137
  • 148
  • 5
    `bash` syntax is a superset of `sh` syntax - the `/bin/sh` executable on your system may provide only standard `sh` functionality, in which `[]`-style tests are not included. – Tim Aug 05 '10 at 01:17
  • 14
    Yes. They are completely different shells. Although, bash was based on and is largely backwards-compatable with sh, and they might actually be the same program on your system, but will still behave differently depending on which name you use. You can have the script run with bash automatically by changing the first line to `#!/bin/bash` and making the file executable, and just running `./choose.sh`. – Tyler McHenry Aug 05 '10 at 01:17
  • @ Tyler McHenry It is mainly the difference between bash and sh in testing variable when using brackets.Thanks. – kit.yang Aug 05 '10 at 01:32
  • 16
    This answer is partially wrong. The fully correct one is the now top-voted by Nietzche-jou. `sh` supports `[`. `==` works in Bash built-ins `[[`, `test` and `[` while [a single `=` is required by POSIX version of `[` and `test`](http://pubs.opengroup.org/onlinepubs/009695399/utilities/test.html). (Bash manual says the same, BTW.) Requiring Bash because of this is needless harm to portability. Command name is `[` or `test`, `]` is just non-necessary, unused last parameter. Many shells implement these as built-ins, [as Wikipedia says](http://en.wikipedia.org/wiki/Test_%28Unix%29). – Palec Feb 09 '14 at 23:28
  • 1
    @Palec: while the answer is not as extensive as Nietzche-jou's answer, I do not see what would be wrong about it? I never mentioned anything about brackets :) +1 for your comment regardless! – Wolph Feb 11 '14 at 08:05
  • 4
    @Wolph By writing partially wrong I meant two things. First, the problem is not in Bash vs sh syntax, both can call `[` command correctly. It is in syntax of the `[` command parameters, which is not sh’s business. Second, Bash is overkill for such a job, especially when a much simpler solution exists. This is not really wrong as it solves the problem too, but I think it is generally bad advice. This leads beginners to false conclusion that Bash solves their problems. It has many unportable extensions over POSIX-required features. I believe we should lead beginners to writing portable programs. – Palec Feb 11 '14 at 09:32
  • smart answer, cause `sh` is actually really limited compare to the born again shell(bash). – william.eyidi Jul 29 '15 at 19:08
  • I wonder how this works under the hood. test maps to `usr/bin/test` in both shells. How doth test tell its shell? – personal_cloud Sep 21 '17 at 17:31
  • @personal_cloud Many (most?) shells actually override `test` with a built-in command. Try `which test` to see what the shell will actually run. The reason is that executing internal code is much faster than launching a new external process. – Wolph Sep 21 '17 at 22:37
  • @Wolph Sorry, I was vague. What I meant is that `which test` returns `/usr/bin/test` in both shells on Ubuntu 14, CentOS 7.3, and Debian 4.3.3-5. – personal_cloud Sep 21 '17 at 22:40
  • 1
    @personal_cloud it seems which behaves different in bash (when compared to zsh), try `type test` instead :) For bash it returns `test is a shell builtin` – Wolph Sep 22 '17 at 20:15
386

POSIX sh doesn't understand == for string equality, as that is a bash-ism. Use = instead.

The other people saying that brackets aren't supported by sh are wrong, btw.

Nietzche-jou
  • 14,415
  • 4
  • 34
  • 45
  • 11
    Yes,I try to just replace the "==" for "=",the script can also be executive by "sh ./choose.sh".Both bash and sh support the brackets. – kit.yang Aug 05 '10 at 01:28
  • 73
    +1 This is a better answer than unnecessarily using bash, IMO. – John Kugelman Aug 05 '10 at 01:31
  • 3
    In my specific case, My Teamcity agent is running sh by default, and I do not want to change that (it generates other issues). SH is required here, and I think that this is the correct answer for people that must use sh – Nicolas Oliver Nov 15 '17 at 19:28
  • +1 This is the right answer. Anyone looking to solve it for sh should just use single '='. Only reading the top voted reply by Wolph wasted my 3 hours :( – Umer Feb 08 '19 at 13:40
  • 2
    Still a very valuable tip. Lots of docker images have sh and no bash. – Steve Mar 02 '20 at 22:40
9

To execute it with Bash, use #!/bin/bash and chmod it to be executable, then use

./choose.sh
Jack Scott
  • 442
  • 4
  • 10
6

you can use case/esac instead of if/else

case "$choose" in
  [yY]) echo "Yes" && exit;;
  [nN]) echo "No" && exit;;
  * ) echo "wrong input" && exit;;
esac
ghostdog74
  • 327,991
  • 56
  • 259
  • 343
5

you have to use bash instead or rewrite your script using standard sh

sh -c 'test "$choose" = "y" -o "$choose" = "Y"'
Anycorn
  • 50,217
  • 42
  • 167
  • 261
  • I think it is more convenient using "[ ]" instead of "test". It seemed that "bash ./choose.sh" can solve the problem. – kit.yang Aug 05 '10 at 01:12
  • @kit either way, sh is more little portable – Anycorn Aug 05 '10 at 01:19
  • 3
    @kit.yang How are brackets more convenient? The historical confusion caused by people failing to realize that [ is a synonym for test and the continuing errors made through omitted whitespace around the brackets hardly compensate for the single character saved. ("if [ $x = 5 ]" vs "if test $x = 5"; 13 chars vs 14). – William Pursell Aug 08 '10 at 03:17
5

In fact the "[" square opening bracket is just an internal shell alias for the test command.

So you can say:

test -f "/bin/bash" && echo "This system has a bash shell"

or

[ -f "/bin/bash" ] && echo "This system has a bash shell"

... they are equivalent in either sh or bash. Note the requirement to have a closing "]" bracket on the "[" command but other than that "[" is the same as "test". "man test" is a good thing to read.

EdChum
  • 376,765
  • 198
  • 813
  • 562
delatbabel
  • 3,601
  • 24
  • 29
-6

Do not use any reserved keyword as the start of any variable name: eg HOSTNAME will fail as HOST {TYPE|NAME} are reserved