3

Running the following code:

#!/bin/bash

set -o pipefail
set -o errtrace
set -o nounset
set -o errexit

function err_handler ()
{
    local error_code="$?"
    echo "TRAP!"
    echo "error code: $error_code"
    exit
}
trap err_handler ERR

echo "wrong command in if statement"

if xsxsxsxs 
    then
        echo "if result is true"
    else
        echo "if result is false"
fi

echo -e "\nwrong command directly"
xsxsxsxs 

exit


produces the following output:

wrong command in if statement
trap.sh: line 21: xsxsxsxs: command not found
if result is false

wrong command directly
trap.sh: line 29: xsxsxsxs: command not found
TRAP!
error code: 127


How can I trap the 'command not found' error inside the if statement too?

Jolta
  • 2,620
  • 1
  • 29
  • 42
Luca Borrione
  • 16,324
  • 8
  • 52
  • 66

2 Answers2

6

You can't trap ERR for the test in the if

From bash man:

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 !

But you could change this

if xsxsxsxs 
then ..

to this

xsxsxsxs 
if [[ $? -eq 0 ]]
then ..
German Garcia
  • 1,209
  • 14
  • 14
  • **+1** ... absolutely correct, though I think it's not what the OP is after. – ghoti Oct 28 '12 at 04:53
  • @ghoti ..I think you're right. This question made me think if there really wasn't another way closer to what OP wants, but I don't have any other clue. Thanks for the upvote. – German Garcia Oct 28 '12 at 12:13
  • 1
    +1(000) for showing me a hidden feature of the "old-way" of writing a test like this `something ; if [ "$?" -eq 0 ] ....` ! – Olivier Dulac Jul 04 '14 at 12:34
1

Unluckly, German Garcia is right, so I wrote a workaround.
Any suggestion or improvement is more than welcome thanks.

Here's what I did so far:

#!/bin/bash

set -o pipefail
set -o errtrace
set -o nounset
set -o errexit

declare stderr_log="/dev/shm/stderr.log"
exec 2>"${stderr_log}"

function err_handler ()
{
    local error_code=${1:-$?}
    echo "TRAP! ${error_code}"
    echo "exit status: $error_code"

    stderr=$( tail -n 1 "$stderr_log" )
    echo "error message: $stderr"


    echo "" > "${stderr_log}"
    echo "Normally I would exit now but I carry on the demo instead"

    # uncomemment the following two lines to exit now.
    # rm "${stderr_log}"
    # exit "${error_code}"
}
trap err_handler ERR

function check ()
{
    local params=( "$@" )

    local result=0

    local statement=''
    for param in "${params[@]}"
    do
        local regex='\s+'
        if [[ ${param} =~ ${regex} ]]
            then
                param=$( echo "${param}" | sed 's/"/\\"/g' )
                param="\"$param\""
        fi
        statement="${statement} $param"
    done

    eval "if $statement; then result=1; fi"

    stderr=$( tail -n 1 "$stderr_log" )

    ($statement); local error_code="$?"

    test -n "$stderr" && err_handler "${error_code}"
    test $result = 1 && [[ $( echo "1" ) ]] || [[ $( echo "" ) ]]
}

echo -e "\n1) wrong command in if statement"

if check xsxsxs -d "/etc"
    then
        echo "if returns true"
    else
        echo "if returns false"
fi

echo -e "\n2) right command in if statement"

if check test -d "/etc"
    then
        echo "if returns true"
    else
        echo "if returns false"
fi

echo -e "\n3) wrong command directly"
xsxsxsxs 

exit


Running the above will produce:

1) wrong command in if statement
TRAP!
error code found: 0
error message: trap.sh: line 52: xsxsxs: command not found 
I would exit now but I carry on instead
if returns false

2) right command in if statement
if returns true

3) wrong command directly
TRAP!
error code found: 127
error message: trap.sh: line 77: xsxsxsxs: command not found
I would exit now but I carry on instead

So the idea is basically to create a method called 'check', then to add it before the command to debug in the if statement.
I cannot catch the error code in this way, but it doesn't matter too much as long as I can get a message.

It would be nice to hear from you about that. Thanks

Community
  • 1
  • 1
Luca Borrione
  • 16,324
  • 8
  • 52
  • 66
  • (returning from vacation) Well.. I'm not sure.. this seems as a viable workaround, but has its problems too (e.g. that eval is messing with the spaces in arguments). Maybe you're pushing bash to its limits :) – German Garcia Nov 07 '12 at 18:09
  • @German Garcia I updated tge code to handle params with space in them. thanks for the head up. – Luca Borrione Feb 03 '13 at 17:02
  • I'd move the `rm "${stderr_log}"` part outside of the trap, in a "cleanup ()" function of its own, and trap that one to the usual signals (exit, etc). – Olivier Dulac Jul 04 '14 at 12:36
  • it is dangerous to run such script as root as one could create symlink in /dev/shm/stderr.log that points to arbitrary fail which script would destroy on error. – Hrvoje Špoljar Sep 03 '14 at 09:08
  • 1
    @HrvojeŠpoljar That is not solving the issue too. I found people complaining on TRAP not working for errors or exit inside If statement. but its not working for me if I put even outside any for or conditional statement – Tara Prasad Gurung Mar 24 '19 at 11:46