Bash Script function verification - validation

I am trying to do validation for every function I call in a script and erroring out the whole script if one function fails.
Looking for the best way to do this. I feel I knew a good way to do it at one time, but can't figure it out again.
I can brute force it, but it's nasty. This is how I can get it to work correctly
copy_files $1
if [ "$?" -ne "0" ]; then
error "Copying files"
return -1
fi
This gets pretty ugly since I have a script that goes:
command1
command2
command3
Having to wrap all these commands with returns is not very ideal. :( Please help!

command1 || (error "Cmd1 fail"; return -1); command2 || (error "Cmd2 fail"; return -1);
etc. etc. The || operator means the next command will only execute if the previous command failed. && is the opposite. The brackets group the commands to run.

Since you said you want to do this for every command in a script, you could insert
set -e
at the beginning. That call makes the shell exit immediately if any command returns a status code other than 0. (Exceptions: commands that are part of the test in a conditional statement, and those that are followed by && or || and further commands)

You could do something like this:
good() {
return 0;
}
bad() {
return 5;
}
wrapper() {
$1
rc=$?
if [ "$rc" -ne "0" ]; then
echo "error: $1 returned $rc"
exit -1
fi
}
wrapper bad
wrapper good
or, if you could pass a list of function, like so:
wrapper() {
for f in $*; do
$f
rc=$?
if [ "$rc" -ne "0" ]; then
echo "error: $f returned ${rc}"
return -1
fi
done
return 0;
}
wrapper good bad
if [ "$?" -ne "0" ]; then
#error handling here
exit -1;
fi

Related

exception handling in korn shell

I recently stumbled across some shell script code which is repeatedly doing the same thing over again and I was wondering if this could be a bit simplified.
Basically in a defined function, after each and every call the return code is checked. If it is not 0, some outputs are done and the function exits prematurely. In this cases we willfully use return to escape the function but do not want to stop the whole process by using exit.
e.g.:
function _myFunc {
typeset ret=0
_myFirstSubFunc "TEST"
ret=$?
if [[ $? -ne 0 ]]; then
echo "myFirstSubFunc produced an error: $ret"
return $ret
fi
_mySecondSubFunc "TEST"
ret=$?
if [[ $? -ne 0 ]]; then
echo "mySecondSubFunc produced an error: $ret"
return $ret
fi
return $ret
}
I'd like to reduce this repeated code of
checking the return code
printing out some information if the returncode is != 0
stopping the current function from continuing
to one call, so the code isn't that much cluttered with this checking. The execution of the function should continue if the previous call was successful.
Usually, when I'd like to abort (exit) the whole process, one function will do this trick. But when a value should be returned in the case of an error things get (at least for me) a bit tricky.
I tried to use traps or aliases, unfortunately without success. Additionally I'm limited to ksh88, so it seems I cannot use some conditionals following the call by using ||.
Any ideas, if there is something else so I can reduce this tedious error handling?
Thanks!
function orReport {
if "$#"; then
return
else
ret=$?
echo "$1 produced an error: $ret"
return "$ret"
fi
}
function _myFunc {
orReport _myFirstSubFunc "TEST" || return
orReport _mySecondSubFunc "TEST" || return
}

complicated bash functions returning boolean

I want to do something like this:
function one {
if [ "$1" == "hello" ]; then
return true
fi
return false
}
if [ one hello -o one goodbye ]; then
echo "Well, that's weird."
fi
As written, I know this doesn't work. Bash won't let you return boolean values. You can return 0 or 1. But bash doesn't seem to like using 0 or 1 as boolean expressions. So instead I have to return 0 instead of true and 1 for false. I can live with that. But then my if becomes:
if [ one hello -eq 0 -o one goodbye -eq 0 ] ...
Which I can do, but it's awkward. But THAT doesn't work, either, because test doesn't want to call my function with an argument.
if [ `one hello` -eq 0 -o `one goodbye` -eq 0 ] ...
Finally, I think that version works. But it's fugly.
Is there an elegant way to:
Have a bash function that returns true/false
And it takes arguments
And then it gets used in a complex if-statement (there's some combination of -o / -a between the [] )
I'd really like to have some way of writing:
if [ myfunc1 $somearg -a $myfunc2 $someotherarg ]; then ...
For now, I'm doing it the fugly way.
You don't really write predicates like this. Instead, you set the exit status to 0 if a function succeeds, or non-zero value if it fails. The if statement then checks the exit status directly.
one () {
if [ "$1" = "hello" ]; then
return 0
fi
return 1
}
if one hello || one goodbye; then
echo "Well, that's weird."
fi
Since [ itself is a command that has a 0 exit status when the test is true and 1 if false, you can define one as simply
one () {
[ "$1" = "hello" ]
}
If no return statement is encountered, the exit status of a function is the exit status of the last command to execute.
Functions don't return true or false. Instead, they succeed or fail. if checks to see if a command succeeded or not. [ is a very common command that is used in shell scripts, but it misleads many into believing it is part of the grammar. It is not. You can simply write:
if one hello || one goodbye; then
echo "Well, that's weird."
fi
You don't want to use -a or -o. (Those are typically passed as arguments to [, but are effectively deprecated in that usage.) Instead, use the shell operators && and ||.
The fun thing about the strings "true" and "false" is that they are also shell builtin commands that have the expected exit status:
function one {
[[ "$1" == "hello" ]] && echo true || echo false
}
a=$(one hello)
b=$(one goodbye)
if $a || $b; then ...
But, don't do this. Keep it simple as other answers advice.
Like this?
one () {
if [ "$1" = "hello" ]; then
echo true; return 0
fi
echo false; return 1
}
$ one hello && one bye || one hello
true
false
true
Use the builtin commands, /bin/true and /bin/false:
/bin/true always returns 0.
/bin/false always returns 1.
function one {
[[ "$1" == "hello" ]] && /bin/true || /bin/false
}
if one hello || one goodbye; then
# do stuff…
fi;
Because they’re builtins under the /bin (/usr/bin) directory, which will likely be in your $PATH, you can simply call true or false:
function one {
[[ "$1" == "hello" ]] && true || false
}
if one hello || one goodbye; then
# do stuff…
fi;
Some of the answers above almost got it. Though, you wouldn’t want to echo the words, “true” or “false,” as strings—that does nothing in relation to the question. You want to call the commands that were made for this very purpose.

How to return from sourced bash script automatically on any error?

I have a bash script which is only meant to used be when sourced.
I want to return from it automatically on any error, similar to what set -e does.
However setting set -e doesn't work for me because it will also exit the users shell.
Right now I'm handling returning manually like this command || return 1, for each command.
You can also use command || true or command || return.
If your requirement is something different, please update more precisely.
You can use trap. E.g.:
// foo.sh
function func() {
trap 'if [ $? -ne 0 ]; then echo "Trapped!"; return ; fi' DEBUG
echo 'foo'
find -name "foo" . 2> /dev/null
echo 'bar'
}
func
Two notes. First, the trap needs to be inside the function as shown. It won't work if it's just inside the script.
Two, there is a significant limitation. Even if you set the return to the trap (e.g., return 1), while func exists after the bad find command, $? is still zero, no matter what. I'm not sure if there's a way around that, so if it's important to preserve the exit value of the failed command, this may not work.
E.g., if you had:
func
func_return=$?
echo "return value is: $func_return"
func_return will always be zero. I've played around with trying to get the exit value of the failed command to pass out of the function trap and into the function exit value, but have not found a way to do it.
If you need to preserve the return value, you could update a global variable inside the debug trap.
If I understand well, you can set -e locally in each function.
cat sourced
f1 () {
local -
set -e
[ "$1" -eq "$1" ] 2> /dev/null && echo "$1"
}
cat script.sh
. sourced
param='bad'
ret=$(f1 "$param")
[ $? -eq 0 ] && echo "result = $ret" || \
echo "error in sourced file with param $param"
param=3
ret=$(f1 "$param")
[ $? -eq 0 ] && echo "result = $ret" || \
echo "error in sourced file with param $param"

Bash: How to return from parent function if child function failed?

I want to clean up some of my code, for that I want to clean the check return code status check I make after each command. If the command fails I return mid function to the parent function.
If I'll take this code inside a function then nothing happens as the return command will be inside the new-child function.
Sure gald for some thoughts.
Current status:
a(){
for i in $(cat file.txt)
do
scp $i hostb:/tmp/
if [ $? -ne 0 ]
then
print_failed "SCP failed."
return 1
fi
done
}
Desired:
a(){
for i in $(cat file.txt)
do
scp $i hostb:/tmp/
# continue as usuall unless return code is not 0
check_status $?
done
}
check_status(){
if [ $1 -ne 0 ]
then
print_failed "SCP failed."
return 1
fi
}
As far as I'm aware, there is no implicit way to return from the parent function if the child function failed.
The closest thing I can think of would be something like this:
a () {
while read -r source # don't read lines with "for"!
do
check_status scp "$source" hostb:/tmp/ || return 1
done < file.txt
}
check_status () {
if ! "$#"
then
print_failed "SCP failed."
return 1
fi
}
The role of check_status is to execute a command and print the failure message if it didn't succeed. It also returns 1, so the parent function can use if ! or || to return as well.
There's no way of returning from the parent function except by using global variables, which I would personally avoid if possible.
To be honest though, I don't see the advantage of what you have over this:
a () {
while read -r source
do
if ! scp "$source" hostb:/tmp/
then
print_failed "SCP failed."
return 1
fi
done < file.txt
}

Run Multiple bash commands and return false if one of them fails in bash

I am setting up a script that will execute a series of bundle exec rspec commands and I want it to return false if any of them fail. But I still want it to run all the tests. Is there a shorter way to accomplish this without a bunch of if or test statements?
I'm doing it currently like this, but I'll update the answer with nicer syntax if I'm able to write a bash function to handle this:
#!/bin/sh
set +x
RETVAL=0
command1 || RETVAL=1
command2 || RETVAL=1
command3 || RETVAL=1
exit $RETVAL
This is a for loop that will go through all the return codes and if one failed will exit with the first seen failed return code.
i=0
rc=0
command1
rcode[i]=$?
i=i+1
command2
rcode[i]=$? ... n
for i in "${rcode}"
do
if [ $i -ne 0 ]; then
rc=$i
break
fi
done
exit $rc
Track their exit code in a variable, and exit with it. I added the line number to for troubleshooting what broke.
declare -i r_code=0 # return code
command1 || { r_code+=$?; echo "ERROR at $LINENO
}
command2 || { r_code+=$?; echo "ERROR at $LINENO
}
exit $r_code

Resources