13

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.

3 Answers 3

14

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.

2
  • 6
    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. Commented Jun 6, 2015 at 22:47
  • 2
    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. Commented Apr 6, 2016 at 15:20
8

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
}
4
  • It does not look like the zsh solution has the behavior of set -o pipefail, just set -e. Any way to fix this? Commented Aug 1, 2017 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. Commented Aug 1, 2017 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. Commented Aug 1, 2017 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. Commented Aug 1, 2017 at 17:51
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

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .