199

How would you exit out of a function if a condition is true without killing the whole script, just return back to before you called the function.

Example

# Start script
Do scripty stuff here
Ok now lets call FUNCT
FUNCT
Here is A to come back to

function FUNCT {
  if [ blah is false ]; then
    exit the function and go up to A
  else
    keep running the function
  fi
}
1
  • See this answer for a clean, silent and simple exit. Commented May 1, 2021 at 9:38

5 Answers 5

279

Use:

return [n]

From help return

return: return [n]

Return from a shell function.

Causes a function or sourced script to exit with the return value
specified by N.  If N is omitted, the return status is that of the
last command executed within the function or script.

Exit Status:
Returns N, or failure if the shell is not executing a function or script.
Sign up to request clarification or add additional context in comments.

3 Comments

Note that if you have set -e set at the top of your script and your return 1 or any other number besides 0, your entire script will exit.
@YevgeniyBrikman that's only true if the error in the function is unexpected. If the function is called using e.g. || then it's possible to return a nonzero code and still have the script continue to execute.
@DanPassaro Yup, there are definitely solutions possible, but I just wanted to call out that extra care needs to be taken with set -e and returning non-zero values, as that caught me by surprise in the past.
44

Use return operator:

function FUNCT {
  if [ blah is false ]; then
    return 1 # or return 0, or even you can omit the argument.
  else
    keep running the function
  fi
}

2 Comments

Return 0 exited my terminal. Return 1 works as expected in my case.
@progonka, 0 is success and 1 is failure, so that wouldn't happen unless you had other code branching on the return value.
9

EDIT: The following hack does exit the entire script. It's only useful in interactive settings.

If you want to return from an outer function with an error without exiting you can use this trick:

do-something-complex() {
  # Using `return` here would only return from `fail`, not from `do-something-complex`.
  # Using `exit` would close the entire shell.
  # So we (ab)use a different feature. :)
  fail() { : "${__fail_fast:?$1}"; }

  nested-func() {
      try-this || fail "This didn't work"
      try-that || fail "That didn't work"
  }
  nested-func
}

Trying it out:

$ do-something-complex
try-this: command not found
bash: __fail_fast: This didn't work

This has the added benefit/drawback that you can optionally turn off this feature: __fail_fast=x do-something-complex.

Note that this causes the outermost function to return 1.

13 Comments

Could you explain more on the inner function fail, what is the colon doing here?
The : is a built-in bash operator that is a "no-op". It evaluates the expression but doesn't do anything with it. I'm using it to do variable substitution that will fail if the variable isn't defined, which it obviously isn't.
This answer is sorely lacking in explanation, even after the response to "Could you explain...". It's as if Elliot doesn't want to reveal his "trick".
What's left to explain? It's a variable substitution where we expect the variable to not be defined.
It doesn't explain how variable-substitution achieves the original question's request. Why does this exit the inner function? Why doesn't this return from the outer function, or the script as a whole? Is this specific to bash, or is this described in the POSIX standard somewhere? So on, so forth. (Also, hello fellow El+iot+! Want in on ell.io? :D)
|
-2

My use case is to run the function unless it's already running. I'm doing

mkdir /tmp/nice_exit || return 0

And then at the end of the function

rm -rf /tmp/nice_exit

2 Comments

You might want to use rmdir instead of rm -rf. Your function will also never run anymore if an unexpected error or oversight causes it to stop without deleting that directory (function returns, program or system exits/crashes/is powered off...) For these reasons you might want to use a lock file with flock(1) instead.
@FrenchMasterSword Yeah, it's a function that's running when a container is exiting. So I don't think all that applies. Happy to solve any issues that come up with not running, just always want to make sure it doesn't run again at the same time.
-2

I found the above examples incomplete. And tbh confusing.

function test() { [[ 5 -eq 5 ]] && return 0 || return 1 ; } ; test ; echo $?

function test() { [[ 5 -eq 3 ]] && return 0 || return 1 ; } ; test ; echo $?

will print

0

1

2 Comments

You're merging POSIX and legacy ksh function syntax in a way that's incompatible with both POSIX and legacy ksh. Use funcname() without function to have modern POSIX syntax, or function funcname without () for 80s ksh syntax.
That said, you'd get exactly the same outcome as the first from test() { [[ 5 -eq 5 ]]; }, or the second from test() { ! [[ 5 -eq 5 ]]; } -- all the && and || in this answer is completely redundant. (The default return value of a function is always $?; that's also true when you call return with no arguments)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.