6

I'm working on a bash script and as I've been going I've learned about traps, signals, function return codes and other such features I've not previously used.

I may be thinking about things incorrectly - I'm looking for some advice.

I am setting the following options:

set -o errexit
set -o nounset
set -o noclobber

I've got the following exit and err traps in my bash script:

# Error handler. This function is called anytime an ERR signal is received.
# This function should never be explictly called.
function _trap_error () {
    if [ ! -v _VERBOSE ]; then
        echo "An error has occurred. Exiting."
    else
        _name="$0"                # name of the script
        _lastline="$1"            # argument 1: last line of error occurence
        _lasterr="$2"             # argument 2: error code of last command
        echo "${_name}: line ${_lastline}: exit status of last command: ${_lasterr}"
        exit 1
    fi
}
trap '_trap_error ${LINENO} ${$?}' ERR

# Exit handler. This function is called anytime an EXIT signal is received.
# This function should never be explicitly called.
function _trap_exit () {
    [ -v _POPD ] && popd &> /dev/null
}
trap _trap_exit EXIT

They work much as I'd expect. Rather than inserting error checking into all my functions, I'm attempting to leverage the traps to handle this for me, for example when checking for the existence of a file. If the specified module can't be loaded, I'd like to catch it as an error, display an error message, and exit.

function _module_path () {
    echo "mod.d/$2s/$1/__init__.sh"
}

function _module_exists () {
    [ -f $(_module_path $1 $2) ] && return 0 || return 1
}

function _module_push () {
    _module_exists $1 $2 && _MODULES+=$( _module_path $1 $2 ) || msg "Module $1 does not exist."
}

However, setting the return code to 0 in conjunction with errexit triggers an EXIT signal, which is caught by my exit trap instead. I started trying to figure out if I can manually emit an ERR signal instead, but haven't found an answer and started to wonder if I'm going about this correctly.

Anthon
  • 79,293
nfarrar
  • 391
  • Please look at the following answer, especially if you're using [ -v ]. I think its the best answer I've ever written. Also, what is ${$?} ? I suspect it's your problem. That should be an invalid substitution as far as I can tell. http://unix.stackexchange.com/a/120008/52934 – mikeserv Apr 20 '14 at 01:21
  • From info bash: The ERR trap is not executed if the failed command is part of the command list immediately following a while or until keyword, part of the test in an if statement, part of a command executed in a && or || list, or if the command's return value is being inverted via !. – devnull Apr 20 '14 at 02:55
  • @devnull That's true too - traps are triggered for otherwise unhandled exceptions. A while or until wouldn't trigger as they are explicit tests. While loops don't return a failure code just because their defined parameters run out. But if you want an error code all you have to do is ${unset_var?this is written to stderr} or just false – mikeserv Apr 20 '14 at 03:05
  • @mikeserv "$?" is the error code of the last executed command. – nfarrar Apr 20 '14 at 15:31
  • 1
    Im well aware of that. I just checked and it seems to work, but it is strange. For instance, echo ${$?} works, but n=$? ; echo ${$n} returns a bad substitution error. See what i mean? Don't know why youre putting ${} around it. Please see that other post - it discusses all kinds of ways of generating, handling errors. – mikeserv Apr 20 '14 at 16:13
  • I don't understand what you're asking here. What do you mean by “setting the return code to 0”? This is central to your question but I have no idea what you mean. If you mean exit 0, then that runs the EXIT trap, that's what the EXIT trap is all about, so how is that surprising? Please give an example of a behavior that you observe (with a complete script to reproduce it), and explain what different behavior you want to obtain. – Gilles 'SO- stop being evil' Apr 20 '14 at 22:48
  • @Gilles As you summarized - I'm setting the return code for the function to 0 (function exits successfully) or 1 (function exited unsuccessfully), and (unsurprisingly) EXIT signals are emitted and trapped. The problem is that the EXIT signal doesn't distinguish between good and bad exits (AFAIK). When a good exit is trapped, I simply want to clean up. When a bad exit occurs, I want to display an error message or if they debugging flag is set, a stack trace - these things are done in my ERR handler. Initially I thought returning 1 from a function would emit an ERR signal, but I was wrong. – nfarrar Apr 21 '14 at 14:00
  • @ Gilles I'm now trying to figure out an alternate approach to - basically manually emit an ERR signal when something in a function goes wrong. Or if that's a horrible idea - why, and what should I look into instead. I'm currently trying to understand the information referenced by @mikeserv in his referenced post to see if that solution will work. – nfarrar Apr 21 '14 at 14:03
  • The 1 return code doesnt trigger the trap exactly - the failed test of its value does. You have test it to have a failed test. You can kill a shell though with ${unset?}. So test_var=${?%%*[1-9]} ; ${test_var?failed test, trap triggered} might be useful. Or,more simply, _fn || handle_it would fail for any exit code other than 0. If you want a central err handler your going to need a central launcher handler as well - probably best if theyre the same thing. – mikeserv Apr 21 '14 at 17:01
  • Actually, the above should be ${test_var:?...} - sorry. I suppose you could just set -e and the shell would then automatically fail any non-zero exit return code, but relying on a shell option to handle all exceptions that way is risky business - how can you be certain all called programs follow the zero return=success convention? – mikeserv Apr 25 '14 at 12:50
  • @mikeserv, echo ${$?} works the same as echo $$. – Wildcard Sep 06 '17 at 20:34

0 Answers0