How to make bash abort the execution of a script on syntax error?

11,909

Solution 1

Wrapping the whole into a function seems to do the trick:

#!/bin/bash -e

main () {
readonly a=(1 2)
    # A syntax error is here:
    if (( "${a[#]}" == 2 )); then
        echo ok
    else
        echo not ok
    fi
    echo status $?
    echo 'Bad: has not aborted execution on syntax error!'
}

main "$@"

Result:

$ ./sh-on-syntax-err 
$ ./sh-on-syntax-err line 6: #: syntax error: operand expected (error token is "#")
$ 

Though I have no clue why - maybe someone else can explain?

Solution 2

You are probably mislead about the genuine meaning of set -e. A careful reading of the output of help set shows:

-e  Exit immediately if a command exits with a non-zero status.

So -e is about the exit status of commands being non-zero, not about syntax errors in your script.

In general, it is considered bad practice to use set -e, because all errors (i.e., all non-zero returns from commands) should be smartly handled by the script (think robust script, not the ones that go wild after you enter a filename with a space or that starts with a hypen).

Depending on the type of syntax error, the script might not even be executed at all. I'm not knowledgeable enough in bash to tell exactly what class of syntax errors (if only they can be classified) might lead to an immediate abortion of the script or not. Maybe some Bash gurus will join in and clarify everything.

I only hope I clarified the set -e statement!

About your wish:

I expected such safe behavior from a sensible programming language... perhaps this must be reported as a bug/wish to bash developers

The answer is definitely no! as what you've observed (set -e no responding as you're expecting) is in fact very well documented.

Solution 3

You could make the script check itself by putting something like

bash -n "$0"

near the top of the script -- after set -e but before any significant piece of code.

I have to say this doesn't feel very robust, but if it works for you, perhaps it is acceptable.

Share:
11,909

Related videos on Youtube

imz -- Ivan Zakharyaschev
Author by

imz -- Ivan Zakharyaschev

My name is Ivan Zakharyaschev, Иван Захарьящев, imz.

Updated on September 18, 2022

Comments

  • imz -- Ivan Zakharyaschev
    imz -- Ivan Zakharyaschev over 1 year

    To be on safe side, I'd like bash abort the execution of a script if it encounters a syntax error.

    To my surprise, I can't achieve this. (set -e is not enough.) Example:

    #!/bin/bash
    
    # Do exit on any error:
    set -e
    
    readonly a=(1 2)
    
    # A syntax error is here:
    
    if (( "${a[#]}" == 2 )); then
        echo ok
    else
        echo not ok
    fi
    
    echo status $?
    
    echo 'Bad: has not aborted execution on syntax error!'
    

    Result (bash-3.2.39 or bash-3.2.51):

    $ ./sh-on-syntax-err
    ./sh-on-syntax-err: line 10: #: syntax error: operand expected (error token is "#")
    status 1
    Bad: has not aborted execution on syntax error!
    $ 
    

    Well, we can't check $? after every statement to catch syntax errors.

    (I expected such safe behavior from a sensible programming language... perhaps this must be reported as a bug/wish to bash developers)

    More experiments

    if makes no difference.

    Removing if:

    #!/bin/bash
    
    set -e # exit on any error
    readonly a=(1 2)
    # A syntax error is here:
    (( "${a[#]}" == 2 ))
    echo status $?
    echo 'Bad: has not aborted execution on syntax error!'
    

    Result:

    $ ./sh-on-syntax-err 
    ./sh-on-syntax-err: line 6: #: syntax error: operand expected (error token is "#")
    status 1
    Bad: has not aborted execution on syntax error!
    $ 
    

    Perhaps, it's related to exercise 2 from http://mywiki.wooledge.org/BashFAQ/105 and has something to do with (( )). But I find it still unreasonable to continue executing afte a syntax error.

    No, (( )) makes no difference!

    It behaves bad even without the arithmetic test! Just a simple, basic script:

    #!/bin/bash
    
    set -e # exit on any error
    readonly a=(1 2)
    # A syntax error is here:
    echo "${a[#]}"
    echo status $?
    echo 'Bad: has not aborted execution on syntax error!'
    

    Result:

    $ ./sh-on-syntax-err 
    ./sh-on-syntax-err: line 6: #: syntax error: operand expected (error token is "#")
    status 1
    Bad: has not aborted execution on syntax error!
    $ 
    
  • tripleee
    tripleee almost 11 years
    Now your function definition is parsed and evaluated, and it fails.
  • imz -- Ivan Zakharyaschev
    imz -- Ivan Zakharyaschev almost 11 years
    Nice solution! BTW, it doesn't abort the whole program in this case, too. I've appended echo 'Bad2: has not aborted the execution after bad main!' as the last to your example, and the output is: $ LC_ALL=C ./sh-on-syntax-err ./sh-on-syntax-err: line 6: #: syntax error: operand expected (error token is "#") Bad2: has not aborted the execution after bad main! $
  • imz -- Ivan Zakharyaschev
    imz -- Ivan Zakharyaschev almost 11 years
    @tripleee Yes, it looks like parsing the function fails, so it is not complete, but the whole program is not aborted actually in this case (so it's not the effect of exit-on-error, probably).
  • imz -- Ivan Zakharyaschev
    imz -- Ivan Zakharyaschev almost 11 years
    I meant the absence of such feature is a problem. I didn't want to focus on set -e -- it's just a bit close to my goals, that's why it is mentioned and used here. My question is not about set -e, it's about the unsafety of bash if it can't be made to abort on sytax errors. I'm looking for a way to make it always abort on syntax errors.
  • Daniel S
    Daniel S almost 9 years
    Excellent! Or, without set -e: bash -n "$0" || exit