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.

Petr Skocik
  • 28,816

3 Answers3

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.

yaegashi
  • 12,326
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
}
  • It does not look like the zsh solution has the behavior of set -o pipefail, just set -e. Any way to fix this? – Resigned June 2023 Aug 01 '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. – Resigned June 2023 Aug 01 '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 'SO- stop being evil' Aug 01 '17 at 16:37
  • You're right that the official manual at http://zsh.sourceforge.net/Doc/Release/Options.html is perfectly well up to date. I was looking at https://linux.die.net/man/1/zshoptions from Google, which I should have been more clear about. – Resigned June 2023 Aug 01 '17 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