185

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
}
Atomiklan
  • 5,164
  • 11
  • 40
  • 62

4 Answers4

249

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.
Community
  • 1
  • 1
mohit
  • 5,696
  • 3
  • 24
  • 37
  • 49
    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. – Yevgeniy Brikman May 06 '16 at 10:53
  • 2
    @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. – Dan Passaro Jul 11 '19 at 20:32
  • 3
    @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. – Yevgeniy Brikman Jul 12 '19 at 21:19
38

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
}
Nemanja Boric
  • 21,627
  • 6
  • 67
  • 91
7

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.

Elliot Cameron
  • 5,235
  • 2
  • 27
  • 34
  • 1
    Could you explain more on the inner function `fail`, what is the colon doing here? – brook hong Apr 25 '19 at 02:38
  • 1
    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. – Elliot Cameron Apr 25 '19 at 03:08
  • Thanks. Could I replace the expression to some other expression to check the input parameter of `do-something-complex`? checkPara () { if [ $1 -lt $2 ]; then echo $3; fi; } do-something-complex() { checkPara $# 1 "Some message here to warn user how to use the function." echo "yes" } I would `do-something-complex` show user some message and return immediately if there is no parameter fed to the function. – brook hong Apr 25 '19 at 05:10
  • Yes you could do something like that where `checkPara` would use my `fail` function to exit the whole stack of functions. – Elliot Cameron Apr 25 '19 at 05:13
  • It seems not working. (PS: `code` block does not work in comment on stackoverflow). It keeps running after `checkPara`. – brook hong Apr 25 '19 at 05:16
  • `checkPara () { if [ $1 -lt $2 ]; then echo $3; else fail "The parameter isn't right"; fi; }` – Elliot Cameron Apr 25 '19 at 05:19
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/192341/discussion-between-brook-hong-and-elliot-cameron). – brook hong Apr 25 '19 at 05:21
  • 1
    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". – jdunk Jun 13 '21 at 14:35
  • 1
    What's left to explain? It's a variable substitution where we expect the variable to *not* be defined. – Elliot Cameron Jun 14 '21 at 15:26
  • 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 http://ell.io? :D) – ELLIOTTCABLE Sep 08 '21 at 02:40
0

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
nroose
  • 1,689
  • 2
  • 21
  • 28
  • 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 Nov 03 '22 at 12:36
  • @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. – nroose Nov 03 '22 at 19:18