2

This will cause the script to halt as expected, because grep a b returns a non-zero exit code and the -e option is set:

#!/bin/bash
set -e
grep a b
echo "hi"
$ ./test.sh
grep: b: No such file or directory

However, this doesn't, even though there is a syntax error:

#!/bin/bash

set -e

if [ $a = 'a' ]; then
  echo "true"
else
  echo "false"
fi

echo "hi"
$ ./test.sh
./test.sh: line 5: [: a: binary operator expected
false
hi

Is there a way to halt the script if there is an issue with the syntax passed to if [ ]?

neverendingqs
  • 4,006
  • 3
  • 29
  • 57
  • 2
    always quote your vars: ```if [ "$a" = 'a' ]``` – vgersh99 Apr 15 '21 at 15:23
  • You might also look into the `set -u` or `set -o nounset` option which in this case would bail with `test.sh: line 6: a: unbound variable`. – John Moon Apr 15 '21 at 15:35
  • @vgersh99 that's the point of the question : as it is, `if [ $a = 'a' ] ...` is invalid syntax. OP is asking why `set -e` does not cause the script to exit and instead is going in the second branch of the `if` block – Aserre Apr 15 '21 at 15:36
  • @Aserre wouldn't fixing the root cause/culprit fix it all? – vgersh99 Apr 15 '21 at 15:39
  • 1
    @vgersh99 This could be just a code sample, and not the real code (I may be wrong. I'll let OP confirm) – Aserre Apr 15 '21 at 15:40
  • Does this answer your question? [set -e and short tests](https://stackoverflow.com/questions/6930295/set-e-and-short-tests) – Aserre Apr 15 '21 at 15:45
  • The duplicate I linked is kind of a dupe. Basically, the answer is that `set -e` implementations are a big mess, and don't work with some keywords, like `if`. You can also read more [here](http://mywiki.wooledge.org/BashFAQ/105) – Aserre Apr 15 '21 at 15:46

2 Answers2

4

How do you cause a script to exit early if there is a syntax issue inside if [ ]?

You implement such functionality. Happily it is detectable - test or [ exits with 0 when the expression is true, 1 when the expression is false, but a different exit status is returned when the arguments could not be parsed by test. See posix test EXIT STATUS for reference.

if [ invalid; then
    echo ok
else
   ret=$?
   if ((ret == 1)); then
       echo "It was false"
   else
       echo "Och nuuu - invalid" 
       exit
   fi
fi
   

or you could write your own function wrapper:

# Fun fact: yes `[` is allowed in function names.
# Many other characters are allowed too.
safe[() {
  local ret
  ret=0
  [ "$@" || ret=$?
  if ((ret == 0 || ret == 1)); then
       return "$ret"
  else
       exit 1
  fi
}
if safe[ some expression to check ]; then
     echo 'it is true!'
else
     echo 'it is false!'
fi
KamilCuk
  • 120,984
  • 8
  • 59
  • 111
  • could you please elaborate on / link to a reference for the other characters allowed? i absolutely love `safe[` – kevinnls Apr 15 '21 at 16:43
  • 1
    No i cannot. I looked at [posix](https://pubs.opengroup.org/onlinepubs/009604499/utilities/xcu_chap02.html#tag_02_09_05) and [name](https://pubs.opengroup.org/onlinepubs/009604499/basedefs/xbd_chap03.html#tag_03_230) and [bash name](https://www.gnu.org/software/bash/manual/bash.html#index-name), but they all do not allow it. It's a bash extension and I think only bash source would be the refernce. – KamilCuk Apr 15 '21 at 16:53
1

You can handle this case using compound commands.

Here is your script using that :

#!/bin/bash

set -e

[ $a = "a" ] && echo "true" || { [ $? != 2 ] && echo "false" || false; }

echo "hi"

If there's a wrong syntax when evaluating the initial [] expression, the return value will be 2 and not 1. With that, you can check when [] fails for syntax reason and force the script to halt with false under set -e. If the syntax is right, it is then a classic if/then/else in the ternary form.

Zilog80
  • 2,534
  • 2
  • 15
  • 20
  • 1
    @KamilCuk You're right. I'm too used to old bourne syntax. I'll edit. – Zilog80 Apr 15 '21 at 15:55
  • 1
    i have always wondered about how "open" some conditional statements feel. for example in your snippet, if *wrong syntax... return value will be 2*, is it not better to use `{ [ $? == 2 ] && false || echo "false"; }`. is there some reason `!= 2` is preferred? – kevinnls Apr 15 '21 at 16:42
  • 1
    @kevinnls Your point may be a very good question on SO from a technical point of view. At first, many could think it would be opinion based, but there's rational reasons to choose one way or the other. For me, there's many things to consider, if it's assembly code with a heavy performance goal, it will be the way with the least operand codes involved. If it's shell script or high level langage, readability comes first and it will be the way that bring the most efficient meaning. In this case as exemple,`[ $? != 2 ]` is a way to imply that 2 is an exceptional situation and not an expected one. – Zilog80 Apr 16 '21 at 08:43
  • hmm marking as an exceptional case. hadn't considered readability before. thank you :) – kevinnls Apr 16 '21 at 12:55
  • 1
    @kevinnls May i suggest you to ask on software engineering SE something like "which **technical** criteria are to be considered with conditional statements *order*" ? This question will probably be well received, and we could get many useful insights. – Zilog80 Apr 16 '21 at 13:28
  • 1
    @kevinnls *ordrer* is not the appropriate word, maybe "way" or "direction" could better fit. – Zilog80 Apr 16 '21 at 13:38
  • 1
    @kevinnls FYI, i have asked [this question](https://softwareengineering.stackexchange.com/q/425607/387917) on the software engineering SE. – Zilog80 Apr 20 '21 at 13:50
  • 1
    thank you. i just joined there, so with zero rep i cannot really participate. but I'll be following the question :) – kevinnls Apr 20 '21 at 17:49