Xi 张熹
Xi 张熹

Reputation: 11071

How to exit bash function if error?

I am making a presubmit script. It looks like this:

function presubmit() {
  gradle test android
  gradle test ios
  gradle test server
  git push origin master
}

I want the function to exit, if any of the tests fail so it won't push a bug to git. How?

Upvotes: 47

Views: 31488

Answers (6)

Alexis Wilke
Alexis Wilke

Reputation: 20725

Also others have given ways that map one to one to your case, I think a more generic view is better. Also using the || and && for such is a cryptic way of writing scripts (read: prone to ending up with bugs).

I think the following is much easier to work with long term:

function presubmit() {
  if ! gradle test android
  then
    return 1
  fi

  if ! gradle test ios
  then
    return 1
  fi

  if ! gradle test server
  then
    return 1
  fi

  git push origin master
}

The return from the last command is returned by the function so we do not need to have an if/then there.

In your specific case, to avoid the duplication, you could use a for loop like so:

function presubmit() {
  for name in android ios server
  do
    if ! gradle test ${name}
    then
      return 1
    fi
  done

  git push origin master
}

Now, you may instead want to look at a pre-push hook which would probably be much better since whether you run your script or not, the push won't happen unless the hook succeeds.

Upvotes: 0

Kostanos
Kostanos

Reputation: 10404

I would make script more granular:

#!/bin/bash

function test() {
  gradle test android
  gradle test ios
  gradle test server
}
function push() {
  git push origin master
}
# this subshell runs similar to try/catch
(
  # this flag will make to exit from current subshell on any error inside test or push
  set -e
  test
  push
)
# you catch errors with this if
if [ $? -ne 0 ]; then
  echo "We have error"
  exit $?
fi

We track error only inside test and push. You can add more actions outside of subshell where test and push run. You can also this way add different scope for errors (let consider it as try/catch)

Upvotes: 6

andywineio
andywineio

Reputation: 610

1. Use subshell ( .. ) with set -e; to make it more concise, you can do this:

build() {( set -e        # Fail early
  build_cmd_step_1
  build_cmd_step_2
  build_cmd_step_3
  ...
)}

Then, the function will fail on the first failure and you can intercept the exit status:

build
exit_status=$?
if [ ${exit_status} -ne 0 ]; then
  echo "We have error - build failed!"
  exit "${exit_status}"
fi

2. Alternatively, the && \ chaining inside a function is also good (https://stackoverflow.com/a/51913013/1375784), though it might get bad if you have a bigger function.

Both methods can be good, depending on your use case (in some cases using subshells may cause some unwanted side effects)

Upvotes: 46

Drew Chapin
Drew Chapin

Reputation: 7989

The way I do it is to add && \ after every command in the function (except the last one).

function presubmit() {
    gradle test android && \
    gradle test ios && \
    gradle test server && \
    git push origin master
}

Upvotes: 21

anubhava
anubhava

Reputation: 784878

You can do this:

# declare a wrapper function for gradle
gradle() {
   command gradle "$@" || exit 1
}

presubmit() {
  gradle test android
  gradle test ios
  gradle test server
  git push origin master
}

declare -xf presubmit gradle

Call the function in a subshell as:

( presubmit )

Upvotes: 5

MichaelMMeskhi
MichaelMMeskhi

Reputation: 689

Usually when I call a function and want an error message incase it fails I do this:

presubmit || { echo 'presubmit failed' ; exit 1; }

By adding the || flag, it will determine whether which expression is TRUE.

Hope this helps :)

Upvotes: 4

Related Questions