0

I have a Bash subshell function like this:

f() (
  set -e
  ( exit 1 )
  echo "I should not be printed!"
)

If I invoke it like this:

f

I get no output, as expected.

But if I invoke it like this:

f || true

or like this:

if ! f; then : ; fi

I get "I should not be printed!" This is not what I'd expect. My expectation is that, since I'm defining f using a subshell (f() ( ... ) instead of f() { ... }), this should be equivalent to putting the contents of f into its own shell script, then running it as bash ./f.sh, in which case I get no output.

So, my question is twofold:

  1. Is there a way to get f to always return when it hits that ( exit 1 ) line, regardless of how it's called, while still keeping it a function? (i.e. without putting the contents of f into its own script and invoking it like bash ./f.sh?)

  2. Is set -e not working (a bug in Bash, maybe?), or is this expected behavior? Why does the execution of this function depend on whether it's invoked "bare" (like f) or as part of an expression that checks the return code (like f || true)?

2
  • 1
    Many recommend not using set -e precisely because it's not always clear what will and will not trigger the script to exit. Commented Sep 15, 2022 at 14:48
  • mywiki.wooledge.org/BashFAQ/105. This is a must read for anyone contemplating set -e. Key takeway: don't use it. Commented Sep 15, 2022 at 15:57

1 Answer 1

3

From the bash man page:

If a compound command or shell function executes in a context where -e is being ignored,

f || true and ! f are such contexts

none of the commands executed within the compound command or function body will be affected by the -e setting, even if -e is set and a command returns a failure status.

So even though set -e appears in the body of the function, the setting is ignored due to the context in which the function is executed.

Sign up to request clarification or add additional context in comments.

2 Comments

I guess that makes sense. Though it seems like a major pitfall, I'll have to accept it. Is there a workaround or is the only option to move f to its own script?
IMO, you should stop using set -e and have f return a non-zero exit status that the caller can check to decide if the script needs to exit or not.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.