Explicitly trigger an ERR trap, bash - bash

Is there some way for a bash script to explicitly trigger a trap that is looking for an ERR signal (which is a special signal that can be explicitly called via kill, see https://stackoverflow.com/a/26261518/8236733)?
Have a trap file of the form
#!/bin/bash
error() {#do stuff like alert people via email}
trap 'error ${LINENO} $tablename' ERR
and a script of the form
#!/bin/bash
# trap to catch errors
source '/home/mapr/etl_scripts/clarity/lib.trap.sh'
{#try stuff} || {#catch stuff; exit 1;}
I had thought that the exit 1 would be enough to signal the trap, but this does not seem to be the case. Is there some other way to intentionally trigger the trap from within the script? Thanks.

The ERR trap is only executed when a command run by the shell fails, not when the shell itself exits with non-zero exit status. For your case, you want to use an EXIT handler that tests the exit status.
trap 'rv=$?; if [ "$rv" -ne 0 ]; then error $LINENO $tablename; fi' EXIT

Related

Trap doesn't exit the loop

I'm trying to do a cleanup using trap command. The safe_cancel function is being called when I hit a Ctrl + C but the script isn't being exited. I have to use Ctrl + Z to suspend the script and then kill.
The foo is another script I have in my PATH which returns an exit 1 if it receives an invalid argument.
What am I lacking or doing wrong in this script?
#!/bin/bash
safe_cancel () {
echo "Cancelling..."
# do some cleanup here
exit 1
}
trap safe_cancel 1
while true; do
read -p "Choose an option: " someOption < /dev/tty
foo $someOption
if [[ $? == 0 ]]
then
break
exit 0
fi
done
Additional details:
I'm writing this script for a Git hook. Apparently, git hooks aren't exactly expecting a standard in/out so I have to explicitly use /dev/tty
Edit:
When using this as part of a git hook, I'm receiving the error
read: read error: 0: Input/output error
and it's an infinite loop
Signal 1 is SIGHUP, which is raised if the terminal goes away, for example because you were connected from a remote machine and your session was interrupted because the network disconnected. When you press Ctrl+C, this sends SIGINT.
trap safe_cancel HUP INT
This may or may not be related to the error you get with Git.

Bash ERR signal not trapped in procedures?

Consider the following code:
#!/bin/bash
trap 'echo "ERROR" && exit 2' ERR
proc(){
false
return 0
}
echo START
proc
echo END
The above shows output
START
END
but I would expect the false command to trigger the trap procedure for the ERR signal.
If I put false instead of the call to proc the signal is triggered and output
START
ERROR
is shown as expected. If I put the trap command again at the beginning of proc procedure, it is again being correctly trapped.
How is it so that trapping only works outside of procedures, unless trap command is repeated in the procedure? I could not find any documentation on that.
I got the same behavior on bash versions 3.1.0, 3.2.25, 4.1.17 .
Quoting man bash on FUNCTIONS:
the ERR trap is
not inherited unless the -o errtrace shell option has been enabled.
So, just add
set -o errtrace
to the script and it starts working.

Trap syntax issue in bash

I intend to use trap to execute some clean up code in case of a failure. I have the following code, but it seems to be have some syntactical issues.
#!/bin/bash
set -e
function handle_error {
umount /mnt/chroot
losetup -d $LOOP_DEV1 $LOOP_DEV2
}
trap "{ echo \"$BASH_COMMAND failed with status code $?\"; handle_error; }" ERR
Does any one see an issue with the way the trap has been written. In case of an error the trap does get executed fine but it also throws another unwanted error message below.
/root/myscript.sh: line 60: } ERR with status code 0: command not found
##line 60 is that line of code that exited with a non zero status
How do I write it correctly to avoid the error message? Also what if I had to send arguments $LOOP_DEV1 and $LOOP_DEV2 from the main script to the trap and then to the handle_error function? Right now they are exported as environment variables in the main script. I did some search for trap examples but I couldn't get something similar.
EDIT
I changed the shebang from /bin/sh to /bin/bash. As /bin/sh was already symlinked to bash I did not expect unicorns nor did I see any.
That trap call is creating an interesting recursion, because $BASH_COMMAND (and $?) are being expanded when the trap command executes. However, $BASH_COMMAND at that point is the trap command itself, textually including $BASH_COMMAND (and some quotes and semicolons). Actually figuring out what the command to be executed when the trap fires is an interesting study, but it's not necessary to fix the problem, which you can do like this:
trap '{ echo "$BASH_COMMAND failed with status code $?"; handle_error; }' ERR
Note that replacing " with ' not only avoids immediate parameter expansion, it also avoids have to escape the inner "s.

Is there a goto on error/warning type of deal in bash?

Ok so I saw at work a script which is on OpenVMS using the DCL scripting language that they have a way to do a catch all for warnings or errors etc...
There is a page about it here. http://h71000.www7.hp.com/doc/84final/9996/9996pro_150.html basically its ON warning THEN GOTO sendfailemail or something like that. I was wondering without using a bunch of if statements is there a way to do such a thing in bash?
I am looking for something that can do a trap not just based on ERR but return code. What is nice about vms is that you can do on warning which on vms is exit status 0 vs error which is exit status 2. So I know I can do trap ... ERR but I wanted something that could trap exit status 1 or 2 specifically.
Just to be clear about the "normal" thing to do:
set -e
trap 'echo "Failed"' ERR
echo "Start"
false
echo "End"
In this example, the set -e will cause the script to exit if anything returns non-0 (after running the ERR trap). In unix, non-0 means "error." There is no return value for "warning." So if you go creating special return values for your scripts, you're not going to fit into unix command pipelines correctly. You really, really need to return 0 on success, and anything else should be some kind of failure.
That said, let's say there's some really great reason here, and you have an ecosystem of scripts that have special return values. You can certain do this. Just trap ERR and check $? (note that I've removed set -e since here you exit manually):
trap 'if [ $? == 2 ]; then echo "Failed"; exit 2; fi' ERR
echo "Start"
false
echo "End"
This won't fail, since false returns 1.
If you need this only in a few places and want to be explicit, you can create your own wrapper functions to manage it without too much extra coding.
function run() {
$*
if [ $? == 2 ]; then
echo "Failure"
exit 2
fi
}
run echo Start
run true
run false
run echo "End"
Simple:
if yourcommandhere; then
echo 'No error'
else
echo "Error! Exit status of the last command is $?"
fi

How to make bash execute a script every time it exits?

I want to execute some commands every time I exit from bash, but cannot find a way to do it.
There is a ~/.bash_logout file for when you are logging out, but normally we use interactive shell instead of login shell, so this is not very useful for this purpose.
Is there a way to do this? Thanks!
You can trap the EXIT signal.
exit_handler () {
# code to run on exit
}
trap 'exit_handler' EXIT
Techinically, trap exit_handler EXIT would work as well. I quoted it to emphasize that the first argument to trap is a string that is essentially passed to eval, and not necessarily a single function name. You could just as easily write
trap 'do_this; do_that; if [[ $PICKY == yes ]]; then one_more_thing; fi' EXIT
rather than gather your code into a single function.

Resources