What can go wrong when using `return` before `exit` in a script that may be sourced?

0
6
Asked By MellowPine47 On

I have a Bash script that can either be executed directly or sourced into the current shell. When it finishes, I want it to use `return` if it was sourced and `exit` if it was executed normally.

A common way to detect whether the script is sourced is `(return 0 2>/dev/null)`. That leads to an explicit conditional:

```bash
if (return 0 2>/dev/null); then
return "$exitcode"
else
exit "$exitcode"
fi
```

However, I could also write:

```bash
return "$exitcode" 2>/dev/null
exit "$exitcode"
```

When sourced, the `return` should stop processing the file. When executed directly, the failed `return` should be ignored and the following `exit` should run. Is there any important edge case or unexpected behavior with this shorter approach?

3 Answers

Answered By SilverKite24 On

You can also make the intent explicit with something like `return "$status" 2>/dev/null || exit "$status"`, but the two-command version is effectively relying on the same behavior. Just ensure the status is a valid numeric exit status and that the code is not running inside a function.

Answered By CopperLark8 On

The short form works when those commands are at the top level of the script, outside any function. A top-level `return` succeeds when the file is sourced, so execution stops there. When the file is run directly, `return` fails, its error is hidden, and the following `exit` runs.

The main trap is putting this inside a function. Inside a function, `return` is valid regardless of whether the file was sourced or executed, so it will always leave the function and the `exit` line will never be reached. Also, redirecting stderr hides every error from that `return`, not just the expected “can only return…” error.

QuietRiver32 -

That makes a function-based wrapper less convenient if the file has a lot of top-level structure, although wrapping the main body can still be a reasonable design.

Answered By BrightMoss61 On

Another option is to put the script body in a `main` function and always use `return` inside it. At the bottom, call the function and handle the process status in one place:

```bash
main "$@"
exit $?
```

That keeps the sourced-versus-executed distinction at the call site instead of spreading it throughout the script. If the file is sourced, you may want the bottom-level call to avoid exiting the caller's shell, so the exact wrapper needs to account for that distinction.

Related Questions

LEAVE A REPLY

Please enter your comment!
Please enter your name here

This site uses Akismet to reduce spam. Learn how your comment data is processed.