How to terminate bash function execution - bash

I have several functions in my .bashrc and I want to terminate the whole "run" on error. For example:
function assert_env()
{
var_name=$1
if [ -z "${!var_name}" ]; then
printf "Missing environment variable: $var_name\n"
exit 1
fi
}
function my_test()
{
assert_env "abc"
print $abc
}
If i type my_test in terminal I want to terminate the execution, but this script closes the terminal (as expected - I run exit 1).
How may I terminate the execution without closing the current terminal?
EDIT: To be more specific - I don't want to return from assert_env, I want my following commands from my_test to not be executed if the condition from assert_env is not met, something like C++ assert

Note that exit returns the exit code specified to the shell itself, so using exit 1 would obviously exit your current shell. You need to use return here.
As far as you requirement is considered, just use the exit code from the function assert_env to decide if you want to run further statements. Here using return will cause the current function to go out of scope and what ever code you are returning back to the callee can be used to check if its successful/failure
function assert_env() {
local ret_val=0; var_name=$1
if [ -z "${!var_name}" ]; then
printf "Missing environment variable: $var_name\n"
ret_val=1
fi
return ${ret_val}
}
Now using it
function my_test() {
if ! assert_env "abc"; then
return
fi
# Or could be just written as assert_env "abc" || return
}
This way, if the assert_env returns 0, the if-clause in my_test asserts failure and rest of the code gets executed. And on returning 1 the condition becomes true and the function call is returned without exiting the main scipt.
Note that function keyword is non-standard and not POSIX compliant and may not work across shells. Just drop the keyword if you want to make it portable.

I've found a workaround - execute the function content in a subshell
Example:
function assert_env()
{
var_name=$1
if [ -z "${!var_name}" ]; then
printf "Missing environment variable: $var_name\n"
exit 1
fi
}
function my_test()
{
(
printf "$1\n"
assert_env "abc"
printf $abc
)
}

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
}

How to create efficient error checking in Bash script?

To check the exit code of command in Bash the following logic is used:
if [ $? -ne 0 ]
then
echo "Error occurred"
return 1
fi
My problem is that adding this after every command makes the script very long with multiple copies of the same thing, and very hard to maintain.
The best thing here would be a function, that would be called from all the locations the the exit code needs to be checked. The problem is that the exit command can not be used, because it will kill current process (current Bash session will be killed), so only the return command can be used. But when using the return command, in a called function, the calling function must still check the exit code, and were back to the same problem.
Is there any thing like MACRO in Bash or any other way to the error checking more efficient?
Instead of this:
somecommand
if [ $? -ne 0 ]
then
echo "Error occurred"
return 1
fi
You don't need to write conditions on the $? variable,
you can use the command itself in if statements:
if ! somecommand
then
echo "Error occurred"
return 1
fi
Another alternative is to create a helper function to encapsulate the action on error, and use the || operator after the command to call the function and then return 1 after:
error() {
echo "Error occurred"
}
somecommand || { error; return 1; }
Finally, if you don't really need return 1 and you don't mind to exit the script in case of failure, then you can exit 1 inside the helper function and then the caller code can become more compact:
fatal() {
echo "Error occurred"
exit 1
}
somecommand || fatal

How return the control to a korn shell script when another one is executed and finished [duplicate]

I use "source" inside a bash script, as follows:
#!/bin/bash
source someneatscriptthatendsprematurely.sh
I would like to exit from the someneatscriptthatendsprematurely.sh script, without exiting from the main script.
Any help appreciated!
You need the return statement:
return [n]
Causes a function to exit with the return value specified by n. If n is omitted, the return status is that of the last command executed in the function body. If used outside a function, but during execution of a script by the . (source) command, it causes the shell to stop executing that script and return either n or the exit status of the last command executed within the script as the exit status of the script. If used outside a function and not during execution of a script by ., the return status is false. Any command associated with the RETURN trap is executed before execution resumes after the function or script.
You can see this in action with the following two scripts:
script1.sh:
. script2.sh
echo hello again
script2.sh:
echo hello
return
echo goodbye
When you run script1.sh, you see:
hello
hello again
Is it important that you can change environment variables? Since otherwise you can just execute the script by executing it without source:
someneatscriptthatendsprematurely.sh
I had the same problem just now
I realized that adding a checker function and returning that will not also return the function on its caller for example.
On bash_functions
function install_packer_linux() {
check_wget && check_unzip
wget https://releases.hashicorp.com/packer/1.1.2/packer_1.1.2_linux_amd64.zip
unzip packer_1.1.2_linux_amd64.zip
mv packer ~/.local/bin
rm -f packer_1.1.2_linux_amd64.zip
}
function check_unzip() {
if ! [ -x "$(command -v unzip)" ]; then
echo "Error: unzip is not installed"
return 1
else
return 0
fi
}
function check_wget() {
if ! [ -x "$(command -v wget)" ]; then
echo "Error!: wget is not installed"
return 1
else
return 0
fi
}
$ source ~/.bash_functions
What happens here is since the checkers is the only place its returned so install_packer_linux will still continue
So you can do two things here. Either keep the current format (function calling another function) as is and evaluate using truthy value then return if the values are not truthy or rewrite the checker on the main installer_packer_linux function
Truthy:
function install_packer_linux() {
check_wget && check_unzip || return
wget https://releases.hashicorp.com/packer/1.1.2/packer_1.1.2_linux_amd64.zip
unzip packer_1.1.2_linux_amd64.zip
mv packer ~/.local/bin
rm -f packer_1.1.2_linux_amd64.zip
}
Notice we added || return after the checks and concatenated the checks using &&
so if not both checks are truthy we return the function

Return an exit code without closing shell

I'd like to return an exit code from a BASH script that is called within another script, but could also be called directly. It roughly looks like this:
#!/bin/bash
dq2-get $1
if [ $? -ne 0 ]; then
echo "ERROR: ..."
# EXIT HERE
fi
# extract, do some stuff
# ...
Now in the line EXIT HERE the script should exit and return exit code 1. The problem is that
I cannot use return, because when I forget to source the script instead of calling it, return will not exit, and the rest of the script will be executed and mess things up.
I cannot use exit, because this closes the shell.
I cannot use the nice trick kill -SIGINT $$, because this doesn't allow to return an exit code.
Is there any viable alternative that I have overlooked?
The answer to the question title (not in the body as other answers have addressed) is:
Return an exit code without closing shell
(exit 33)
If you need to have -e active and still avoid exiting the shell with a non-zero exit code, then do:
(exit 33) && true
The true command is never executed but is used to build a compound command that is not exited by the -e shell flag.
That sets the exit code without exiting the shell (nor a sourced script).
For the more complex question of exiting (with an specific exit code) either if executed or sourced:
#!/bin/bash
[ "$BASH_SOURCE" == "$0" ] &&
echo "This file is meant to be sourced, not executed" &&
exit 30
return 88
Will set an exit code of 30 (with an error message) if executed.
And an exit code of 88 if sourced.
Will exit both the execution or the sourcing without affecting the calling shell.
Use this instead of exit or return:
[ $PS1 ] && return || exit;
Works whether sourced or not.
You can use x"${BASH_SOURCE[0]}" == x"$0" to test if the script was sourced or called (false if sourced, true if called) and return or exit accordingly.
Another option is to use a function and put the return values in that and then simply either source the script (source processStatus.sh) or call the script (./processStatus.sh) . For example consider the processStatus.sh script that needs to return a value to the stopProcess.sh script but also needs to be called separately from say the command line without using source (only relevant parts included)
Eg:
check_process ()
{
if [ $1 -eq "50" ]
then
return 1
else
return 0
fi
}
and
source processStatus.sh $1
RET_VALUE=$?
if [ $RET_VALUE -ne "0" ]
then
exit 0
fi
You can use return if you use set -e in the beginning of the script.
If you just want to check if the function returned no errors, I'd rather suggest rewriting your code like this:
#!/bin/bash
set -e # exit program if encountered errors
dq2-get ()
{
# define the function here
# ...
if [ $1 -eq 0 ]
then
return 0
else
return 255
# Note that nothing will execute from this point on,
# because `return` terminates the function.
}
# ...
# lots of code ...
# ...
# Now, the test:
# This won't exit the program.
if $(dq2-get $1); then
echo "No errors, everything's fine"
else
echo "ERROR: ..."
fi
# These commands execute anyway, no matter what
# `dq2-get $1` returns (i.e. {0..255}).
# extract, do some stuff
# ...
Now, the code above won't leave the program if the function dq2-get $1 returns errors. But, implementing the function all by itself will exit the program because of the set -e. The code below describes this situation:
# The function below will stop the program and exit
# if it returns anything other than `0`
# since `set -e` means stop if encountered any errors.
$(dq2-get $1)
# These commands execute ONLY if `dq2-get $1` returns `0`
# extract, do some stuff
# ...
Thanks for the question, my case was to source a file for some setup, but end the script and skip the setup actions if certain conditions were not met.
I had hit the issue of an attempt to use exit() actually causing the closing of my terminal, and found myself here :D
After reviewing the options for the specific solution i just went with something like the below, I also think Deepaks answer is worth reviewing if this approach works in your case.
if [ -z "$REQUIRED_VAR" ]; then
echo "please check/set \$REQUIRED_VAR ..."
echo "skipping logic"
else
echo "starting logic"
doStuff()
echo "completed logic"
fi

How do you return to a sourced bash script?

I use "source" inside a bash script, as follows:
#!/bin/bash
source someneatscriptthatendsprematurely.sh
I would like to exit from the someneatscriptthatendsprematurely.sh script, without exiting from the main script.
Any help appreciated!
You need the return statement:
return [n]
Causes a function to exit with the return value specified by n. If n is omitted, the return status is that of the last command executed in the function body. If used outside a function, but during execution of a script by the . (source) command, it causes the shell to stop executing that script and return either n or the exit status of the last command executed within the script as the exit status of the script. If used outside a function and not during execution of a script by ., the return status is false. Any command associated with the RETURN trap is executed before execution resumes after the function or script.
You can see this in action with the following two scripts:
script1.sh:
. script2.sh
echo hello again
script2.sh:
echo hello
return
echo goodbye
When you run script1.sh, you see:
hello
hello again
Is it important that you can change environment variables? Since otherwise you can just execute the script by executing it without source:
someneatscriptthatendsprematurely.sh
I had the same problem just now
I realized that adding a checker function and returning that will not also return the function on its caller for example.
On bash_functions
function install_packer_linux() {
check_wget && check_unzip
wget https://releases.hashicorp.com/packer/1.1.2/packer_1.1.2_linux_amd64.zip
unzip packer_1.1.2_linux_amd64.zip
mv packer ~/.local/bin
rm -f packer_1.1.2_linux_amd64.zip
}
function check_unzip() {
if ! [ -x "$(command -v unzip)" ]; then
echo "Error: unzip is not installed"
return 1
else
return 0
fi
}
function check_wget() {
if ! [ -x "$(command -v wget)" ]; then
echo "Error!: wget is not installed"
return 1
else
return 0
fi
}
$ source ~/.bash_functions
What happens here is since the checkers is the only place its returned so install_packer_linux will still continue
So you can do two things here. Either keep the current format (function calling another function) as is and evaluate using truthy value then return if the values are not truthy or rewrite the checker on the main installer_packer_linux function
Truthy:
function install_packer_linux() {
check_wget && check_unzip || return
wget https://releases.hashicorp.com/packer/1.1.2/packer_1.1.2_linux_amd64.zip
unzip packer_1.1.2_linux_amd64.zip
mv packer ~/.local/bin
rm -f packer_1.1.2_linux_amd64.zip
}
Notice we added || return after the checks and concatenated the checks using &&
so if not both checks are truthy we return the function

Resources