23

Say I have a bash function like so:

gmx(){
  echo "foo";
}

will this function implicitly return the exit value of the echo command, or is using return necessary?

gmx(){
  echo "foo";
  return $?
}

I assume that the way bash works, the exit status of the final command of the bash function is the one that gets "returned", but not 100% certain.

Alexander Mills
  • 9,330
  • 19
  • 95
  • 180

3 Answers3

22

return does an explicit return from a shell function or "dot script" (a sourced script). If return is not executed, an implicit return is made at the end of the shell function or dot script.

If return is executed without a parameter, it is equivalent of returning the exit status of the most recently executed command.

That is how return works in all POSIX shells.

For example,

gmx () {
  echo 'foo'
  return "$?"
}

is therefore equivalent to

gmx () {
  echo 'foo'
  return
}

which is the same as

gmx () {
  echo 'foo'
}

In general, it is very seldom that you need to use $? at all. It is really only needed if you need to save it for future use, for example if you need to investigate its value multiple times (in which case you would assign its value to a variable and perform a series of tests on that variable).

Stéphane Chazelas
  • 522,931
  • 91
  • 1,010
  • 1,501
Kusalananda
  • 320,670
  • 36
  • 633
  • 936
  • 3
    One downside of using `return` is that for functions defined like `f() (...; cmd; return)`, it prevents the optimisation that a few shells do of running the `cmd` in the same process as the subshell. With many shells, that also means that the exit status of the function doesn't carry the information that `cmd` has been killed when it has (most shells can't retrieve that information anyway). – Stéphane Chazelas May 28 '18 at 12:43
  • 1
    Note that with pdksh and some of its derivatives (like OpenBSD `sh` or `posh`), you'd need `return -- "$?"` if there was a chance that the last command was a function that returned a negative number. `mksh` (also based on pdksh) forbids functions from returning negative values. – Stéphane Chazelas May 28 '18 at 16:55
  • thanks this helps, I guess I don't understand how `return x` functions differently than `exit x`...the only thing I do know is that `return x` will not exit the current process. – Alexander Mills May 28 '18 at 17:51
  • 1
    @AlexanderMills Well, it's what I said: `return` is used to return from a function or a dot script. `exit` does something completely different (terminates a process). – Kusalananda May 28 '18 at 17:53
  • yeah that makes sense I think I am starting to get a better handle on this – Alexander Mills May 28 '18 at 17:55
8

From the bash(1) man page:

When executed, the exit status of a function is the exit status of the last command executed in the body.

Ignacio Vazquez-Abrams
  • 44,857
  • 7
  • 93
  • 100
  • right, and a corollary might be that the return statement is nothing more than the exit status? – Alexander Mills May 28 '18 at 07:17
  • I guess `return` is a builtin command - although `return 1` is different than `exit 1`, etc so – Alexander Mills May 28 '18 at 07:18
  • 2
    "return [n]: Causes a function to stop executing and return the value specified by n to its caller. If n is omitted, the return status is that of the last command executed in the function body." *(ibid)* So, `return` forces the exit status of a function to a specific value if specified. – Ignacio Vazquez-Abrams May 28 '18 at 07:21
  • 1
    @AlexandwrMills Yes, `return` and `exit` are both built-ins, except `return` can only be used within function. You can't terminate a script with `return`. Exit status is value that command returns. `return` is command that returns that value. So "return statement is nothing more than the exit status" is just not quite accurate. One is a value, the other is command plus value. – Sergiy Kolodyazhnyy May 28 '18 at 07:31
  • Yeah I guess return is special type of exit command, that prevents a terminal window from closing or something. there must be some reason `return` exists. – Alexander Mills May 28 '18 at 07:43
  • 1
    @AlexanderMills, `return` returns from the function, `exit` exits the whole shell. It's exactly the same as in, say C with `return` vs. `exit(n)`, or `return` vs. `sys.exit()` in Python. – ilkkachu May 28 '18 at 07:56
3

I'll just add a few notes of caution to the answers already provided:

  • Even though return has a very special meaning to the shell, from a syntax point of view, it is a shell builtin command and a return statement is parsed like any other simple command. So, that means that like in the argument of any other command, $? when unquoted, would be subject to split+glob

    So you need to quote that $? to avoid it:

    return "$?"
    
  • return usually doesn't accept any option (ksh93's accepts the usual --help, --man, --author... though). The only argument it expects (optional) is the return code. The range of accepted return codes varies from shell to shell, and whether any value outside 0..255 is properly reflected in $? also varies from shell to shell. See Default exit code when process is terminated? for details on that.

    Most shells accept negative numbers (after all, the argument passed to the _exit()/exitgroup() system call is an int, so with values encompassing at least -231 to 231-1, so it only makes sense that shells accept the same range for its functions).

    Most shells use the waitpid() and co. API to retrieve that exit status however in which case, it's truncated to a number between 0 and 255 when stored in $?. Even though invoking a function does not involve spawning a process and used waitpid() to retrieve its exit status as all is done in the same process, many shells also mimic that waitpid() behaviour when invoking functions. Which means that even if you call return with a negative value, $? will contain a positive number.

    Now, among those shells whose return accepts negative numbers (ksh88, ksh93, bash, zsh, pdksh and derivatives other than mksh, yash), there are a few (pdksh and yash) which need it written as return -- -123 as otherwise that -123 is taken as three -1, -2, -3 invalid options.

    As pdksh and its derivatives (like OpenBSD sh or posh) preserve the negative number in $?, that means that doing return "$?" would fail when $? contains a negative number (which would happen when the last run command was a function that returned a negative number).

    So return -- "$?" would be better in those shells. However note that while supported by most shells, that syntax is not POSIX and in practice not supported by mksh and ash derivatives.

    So, to sum up, with pdksh-based shells, you may use negative numbers in arguments to functions, but if you do, return "$@" won't work. In other shells, return "$@" will work and you should avoid using negative numbers (or numbers outside of 0..255) as arguments to return.

  • In all shells that I know, calling return from inside a subshell running inside a function will cause the subshell to exit (with the provided exit status if any or that of the last command run), but will not otherwise cause a return from the function (to me, it's unclear whether POSIX gives you that warranty, some argue that exit should be used instead to exit subshells inside functions). For instance

    f() {
      (return 3)
      echo "still inside f. Exit status: $?"
    }
    f
    echo "f exit status: $?"
    

    will output:

    still inside f. Exit status: 3
    f exit status: 0
    
Stéphane Chazelas
  • 522,931
  • 91
  • 1,010
  • 1,501
  • How could the shell built in `$?` ever not be a numeric value? – BrainStone Feb 23 '22 at 10:18
  • 1
    @BrainStone, numeric or nor is not relevant here. In the split+glob operator, the split part is being done based on the current value of `$IFS`. So, there can be splitting in contexts where `$IFS` contains digits. See also: [Security implications of forgetting to quote a variable in bash/POSIX shells](//unix.stackexchange.com/q/171346) – Stéphane Chazelas Feb 23 '22 at 10:51