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)?

Brandon
  • 1,336
  • 3
  • 10
  • 38
  • 1
    Many recommend not using `set -e` precisely *because* it's not always clear what will and will not trigger the script to exit. – chepner Sep 15 '22 at 14:48
  • https://mywiki.wooledge.org/BashFAQ/105. This is a must read for anyone contemplating `set -e`. Key takeway: don't use it. – William Pursell Sep 15 '22 at 15:57

1 Answers1

2

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.

chepner
  • 497,756
  • 71
  • 530
  • 681
  • 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? – Brandon Sep 15 '22 at 14:53
  • 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. – chepner Sep 15 '22 at 15:17