Bash error handling to variable - bash

I have a Setup that doesn't work at the moment:
Lets say I have a program.sh
source path/service_x.sh; install_service_x || error_exit
source path/service_y.sh; install_service_y || error_exit
[...]
A service_y.sh with the function install_service_y
install_service_y()
{
[doing funny stuff that likes to fail ;)]
}
An error.sh
with the function error_exit inside which is doing many stuff.
Example:
error_exit()
{
echo "{$error}"
[...]
echo "Here are the error Logs. Error: {$error} appeared" | mutt -a "a_log_file" -s "failed to install service" -- error#yxz.com
}
So, I'm running the program.sh and install_service_y fails,
I want to have that error message stored to a variable $error to use it in the error_exit function of the error.sh
I really hope that you can understand my setup and whats going wrong.

Related

Getting an error for unexpected else in bash

[SOLVED]
I'm pretty new tho bash-/shell-scripting and trying setup a check for ip address on a server which gets about once a week a new ip.
The script will then send the new ip to the users.
My problem is, that I'm getting a syntax-error in the last if-else statement for "unexpected" else and can wrap my head around why.
My first iteration didn't use functions, but instead one multi lined if-else which got me the same error. The functions on their own seem to work just fine.
#!/bin/bash
# script to send the new server ip to the users
# get the recent ip address of the system
new_ip=$(ip route get 8.8.8.8 | awk -F"src " 'NR==1{split($2,a," ");print a[1]}')
file=old_ip.txt
function ip_mail(){
source $file
if [ $new_ip != $old_ip ]
then
# email-address changed for obvious reasons
mail -s "New Server IP" [hidden]#[hidden].com <<< "$new_ip"
echo "old_ip=$new_ip" > old_ip.txt
exit 0
fi
exit 0
}
function set_old(){
touch old_ip.txt
echo "old_ip=$new_ip" > old_ip.txt
exit 0
}
if [ $file ]
then
ip_mail()
else
set_old()
fi

syntax error when assigning to command result to variable

I'm doing some basic unit testing with the shunit2 unit test framework.
I'm getting the error " syntax error near unexpected token `nodeError=$( node "node_fake_returns/return_error.js" )" on the first line of my function. the function is as follows:
function testHandleNodeReturnError{
nodeError=$( node "./node_fake_returns/return_error.js" )
if [ grep -i "Error" <<< "$nodeError" ]; then
assertTrue "true"
fi
}
It is suppose to run a node script that returns an error message to stdout, then assign that output to a variable. Only this first line in the function is important.
I'm quite new to bash and I've messed with the formatting of this line, mostly just adding spaces in different places, but I can't seem to find what's causing the syntax error. This is probably pretty simple but if somebody could show me what might be wrong I would be greatful.
Thanks!
By pasting your code to shellcheck I was left with:
function testHandleNodeReturnError{
^-- SC1095: You need a space or linefeed between the function name and body.
Which is quite literal. You need a space there.
function testHandleNodeReturnError
Using function keyword is deprecated. Just use function_name() { function_body; }.
if [ grep -i "Error" <<< "$nodeError" ]; then
This is very wrong. This is outputting the content of nodeError variable to standard input of [ command. The [ is a command, a executable, just like grep, it's an alias to test program. Then it runs [ comamnd with grep, -i, "Error" and ] as 4 of it's arguments. You don't want that. If you want to check for Error string, just use grep's exit status:
So do:
testHandleNodeReturnError() {
nodeError=$(node "./node_fake_returns/return_error.js")
if grep -q -i "Error" <<<"$nodeError"; then
assertTrue "true"
fi
}

Try/catch like: trigger URL on bash error

I would have something like a big try/catch in a bash script (I would like to trigger an URL if something went wrong). Something like this:
Do task 1
Do task 2
...
Do task 99
If any of this task has failed, stop the script (don't execute task >= 5 if task 4 failed) then trigger an URL (with curl or whatever)
I know set -e exists, but it just stops the script (it does the half job). Maybe there is something with trap, but I did not understand what I read about this. Is there any simple example for this case?
My question is obviously not about trigger the URL, but how to catch error then run an other part of the script.
Use set -e and you can trap the ERR pseudosignal and execute your curl statement if you script exits with an error. If all the tasks succeed, the ERR trap will not be triggered.
set -e
trap on_error ERR
on_error () {
curl $some_url
}
task_1
task_2
# ...
task_last
trap trap_exit EXIT
trap_exit() {
CODE=$?
if [ $CODE -ne 0 ]; then
echo "Failed! Return code: $CODE"
fi
}
This will execute trap_exit whenever your script ends (the EXIT argument) and check if something broke (the $?part).

How to get the real line number of a failing Bash command?

In the process of coming up with a way to catch errors in my Bash scripts, I've been experimenting with "set -e", "set -E", and the "trap" command. In the process, I've discovered some strange behavior in how $LINENO is evaluated in the context of functions. First, here's a stripped down version of how I'm trying to log errors:
#!/bin/bash
set -E
trap 'echo Failed on line: $LINENO at command: $BASH_COMMAND && exit $?' ERR
Now, the behavior is different based on where the failure occurs. For example, if I follow the above with:
echo "Should fail at: $((LINENO + 1))"
false
I get the following output:
Should fail at: 6
Failed on line: 6 at command: false
Everything is as expected. Line 6 is the line containing the single command "false". But if I wrap up my failing command in a function and call it like this:
function failure {
echo "Should fail at $((LINENO + 1))"
false
}
failure
Then I get the following output:
Should fail at 7
Failed on line: 5 at command: false
As you can see, $BASH_COMMAND contains the correct failing command: "false", but $LINENO is reporting the first line of the "failure" function definition as the current command. That makes no sense to me. Is there a way to get the line number of the line referenced in $BASH_COMMAND?
It's possible this behavior is specific to older versions of Bash. I'm stuck on 3.2.51 for the time being. If the behavior has changed in later releases, it would still be nice to know if there's a workaround to get the value I want on 3.2.51.
EDIT: I'm afraid some people are confused because I broke up my example into chunks. Let me try to clarify what I have, what I'm getting, and what I want.
This is my script:
#!/bin/bash
set -E
function handle_error {
local retval=$?
local line=$1
echo "Failed at $line: $BASH_COMMAND"
exit $retval
}
trap 'handle_error $LINENO' ERR
function fail {
echo "I expect the next line to be the failing line: $((LINENO + 1))"
command_that_fails
}
fail
Now, what I expect is the following output:
I expect the next line to be the failing line: 14
Failed at 14: command_that_fails
Now, what I get is the following output:
I expect the next line to be the failing line: 14
Failed at 12: command_that_fails
BUT line 12 is not command_that_fails. Line 12 is function fail {, which is somewhat less helpful. I have also examined the ${BASH_LINENO[#]} array, and it does not have an entry for line 14.
For bash releases prior to 4.1, a special level of awful, hacky, performance-killing hell is needed to work around an issue wherein, on errors, the system jumps back to the function definition point before invoking an error handler.
#!/bin/bash
set -E
set -o functrace
function handle_error {
local retval=$?
local line=${last_lineno:-$1}
echo "Failed at $line: $BASH_COMMAND"
echo "Trace: " "$#"
exit $retval
}
if (( ${BASH_VERSION%%.*} <= 3 )) || [[ ${BASH_VERSION%.*} = 4.0 ]]; then
trap '[[ $FUNCNAME = handle_error ]] || { last_lineno=$real_lineno; real_lineno=$LINENO; }' DEBUG
fi
trap 'handle_error $LINENO ${BASH_LINENO[#]}' ERR
fail() {
echo "I expect the next line to be the failing line: $((LINENO + 1))"
command_that_fails
}
fail
BASH_LINENO is an array. You can refer to different values in it: ${BASH_LINENO[1]}, ${BASH_LINENO[2]}, etc. to back up the stack. (Positions in this array line up with those in the BASH_SOURCE array, if you want to get fancy and actually print a stack trace).
Even better, though, you can just inject the correct line number in your trap:
failure() {
local lineno=$1
echo "Failed at $lineno"
}
trap 'failure ${LINENO}' ERR
You might also find my prior answer at https://stackoverflow.com/a/185900/14122 (with a more complete error-handling example) interesting.
That behaviour is very reasonable.
The whole picture of the call stack provides comprehensive information whenever an error occurs. Your example had demonstrated a good error message; you could see where the an error actually occurred and which line triggered the function, etc.
If the interpreter/compiler can't precisely indicate where the error actually occurs, you could be more easily confused.

bash exit status always 0

I'm experiencing this weird issue where my exit status always return 0 even when it didn't execute successfully.
I want to output the exit status on my prompt with the following code:
function status() {
echo $?
}
export PS1="\$(status)>"
When I run this, I get the following output
0❯ pwd
/Users/tringuyen
0❯ ad
bash: ad: command not found
0❯ echo $?
127
clearly the second last command ad didn't return a 0 status code. However that's what I got from the prompt.
Does anyone know what might be going on here?
EDIT 6/20 11:57AM: The issue seems to be that $? is always 0 no matter what, except there was an error within the .bashrc file itself, which will cause it to return a value different from 0.
Does the following work for you with your bash version?
export PS1="\$?>"
I use the following in my $PS1:
PS1="\`if [ \$? = 0 ]; then echo \[\e[33m\]^_^\[\e[0m\]; else echo \[\e[31m\]\$? O_O\[\e[0m\]; fi\`"
Src: https://github.com/sanmiguel/dotfiles/blob/master/bash/bash_functions.symlink#L63
I also had similar problem but my function looked different. The problem was, I was missing semicolon ";" after VAR=$?
OLD:
function status() {
VAR=$?
echo $VAR
}
Always returned Zero no matter what.
NEW:
function status() {
VAR=$?;
echo VAR;
}
Now returned proper return value.
export PS1="\$(status)>"

Resources