6

Is there such a feature or can it be emulated reasonably easily? I want the same behavior except it should return where set -e would have caused a call to exit.

7

Sub shell might be useful.

func() {(set -e
        echo a
        ehco b
        echo c
)}

func
func
func

This script produces:

a
script.sh: line 3: ehco: command not found
a
script.sh: line 3: ehco: command not found
a
script.sh: line 3: ehco: command not found

Alternatively you might be interested in this try/catch implemetation in bash.

  • 3
    No need for the braces in this case: func () ( … ) is equivalent. Note that since the function is executed in a subshell, its variable assignments, redirections, etc. won't affect the caller. – Gilles Jun 6 '15 at 22:47
  • 1
    This fails to early-out if you run func && foo, if func then ... fi or similar constructs which depend on the exit status of func. See explanation. – John Mellor Apr 6 '16 at 15:20
6

With ksh93, if you use ksh's own syntax for funtion definition (function f {...;}), then options and traps have a local scope. So there, you can do:

function f {
  trap 'return 99' ERR
  set -e
  echo foo
  false
  echo never output
}
f
echo "f exited with status $?"

Which gives:

foo
f exited with status 99
6

You can set the ERR trap, which is executed whenever a command returns a nonzero status.

In bash, it's a bit fiddly because traps are not local to functions. By default, traps are not inherited by subshells or subfunctions; call set -E to change this. Here's some demo code.

#!/bin/bash

subroutine () {
  false
  echo "Subroutine continued after status $?"
}

trapper () {
  local i ret
  trap 'ret=$?
        echo "Command $BASH_COMMAND returned status $ret"
        for ((i=0; i<$((${#FUNCNAME[@]}-1)); i++)) do
          echo "  from ${FUNCNAME[$i]} at ${BASH_SOURCE[$((i+1))]} line ${BASH_LINENO[$((i+1))]}"
        done
        return $ret' ERR
  trap 'ret=$?; trap - ERR RETURN; return $ret' RETURN
  echo ok
  subroutine
  echo "By default, the ERR trap is not inherited"
  false
  echo "You won't see this"
}

main () {
  trapper
  echo "trapper returned status $?"
  false
  echo "still there"
}

main

In zsh, you can define the TRAPZERR function instead of setting a trap for ERR (for compatibility) or ZERR (on systems with a signal called SIGERR — I don't know of any). But if all you want to do is return immediately (and not e.g. print an error message), it's simpler: just set the err_return option. Options are global by default; set the local_options option to make them local to the calling function (if the local_options option is set when a function returns, the options from the time the function was called are restored). Traps are also global by default. Options and traps are inherited in function calls.

myfunction () {
  setopt local_options err_return
  false
  echo not executed
}
  • It does not look like the zsh solution has the behavior of set -o pipefail, just set -e. Any way to fix this? – Radon Rosborough Aug 1 '17 at 16:09
  • Oh, silly me assuming the online docs would be up to date. Recent versions of zsh have a setopt pipe_fail which works just fine. – Radon Rosborough Aug 1 '17 at 16:19
  • @RadonRosborough pipefail was outside the scope of the question. In bash, ksh and zsh you can do set -o pipefail (note that this isn't local to the function, except in zsh if you call setopt local_options in the function). This is not new and the online documentation is up-to-date, but not very easy to read. – Gilles Aug 1 '17 at 16:37
  • You're right that the official manual at zsh.sourceforge.net/Doc/Release/Options.html is perfectly well up to date. I was looking at linux.die.net/man/1/zshoptions from Google, which I should have been more clear about. – Radon Rosborough Aug 1 '17 at 17:51

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.