Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Abort bash script using a function

Tags:

bash

shell

I try to make a function which can interrupt the script execution (due to fatal error):

quit() {
  echo -e "[ERROR]" | log
  exit 1
}

Call example:

if [ "$#" -eq 1 ]; then
    # Do stuff
else
    echo -e "function getValue: wrong parameters" | log
    quit
fi

Function quit is called (echo in the logfile) but the script keeps going. I've read that exit only terminate the subshell (is that true?) which means that it terminates the quit function but not the entire script.

For now, I prefer not use return code in quit method as it implies a lot of code refactoring.

Is there a way to stop the script from the quit function?

EDIT: full example of a case where the error appears:

#!/bin/bash

logfile="./testQuit_log"

quit() {
  echo "quit" | log
  exit 1
}

log() {
  read data
  echo -e "$data" | tee -a "$logfile"
}


foo() {
  if [ "$#" -ne 1 ]; then
    echo "foo error" | log
    quit
  fi
  echo "res"
}

rm $logfile

var=`foo p1 p2`
var2=`foo p1`

echo "never echo that!" | log

EDIT2: it works correctly when I switch these lines:

var=`foo p1 p2`
var2=`foo p1`

with

var= foo p1 p2
var2= foo p1

Any explanation? Is that because of the subshell?

like image 490
DavidL Avatar asked Mar 15 '23 12:03

DavidL


1 Answers

As it has been outlined in the question's comment section, using exit in a subshell will only exit the subshell and it is not easy to work around this limitation. Luckily, exiting from a subshell or even a function in the same shell is not the best idea anyway:

A good pattern to solve the problem of handling an error on a lower level (like a function or subshell) in a language without exceptions is to return the error instead of terminating the program directly from the lower level:

foo() {
   if [ "$#" -ne 1 ]; then
       echo "foo error" | log
       return 1
   else
       echo "res"
       # return 0 is the default
   fi
} 

This allows control flow to return to the highest level even on error, which is generally considered a good thing (and will incredibly ease debugging complex programs). You can use your function like this:

var=$( foo p1 p2 ) || exit 1
var2=$( foo p1 ) || exit 1

Just to be clear, the || branch is not entered if the assignment fails (it won't), but if the command line inside the command substitution ($( )) returns a non-zero exit code.

Note that $( ) should be used for command substitution instead of backticks, see this related question.

like image 132
Michael Jaros Avatar answered Mar 24 '23 23:03

Michael Jaros