Function Return Values Within Bash If Statements

What is the proper way to test a Bash function's return value?

If it was the exit code and not the result, you could just use

if func arg; then ...

If you cannot make the function return a proper exit code (with return N), and you have to use string results, use Alex Gitelman's answer.

$ help if:

if: if COMMANDS; then COMMANDS; [ elif COMMANDS; then COMMANDS; ]… [ else COMMANDS; ] fi

Execute commands based on conditional.

The if COMMANDS list is executed. If its exit status is zero, then the
then COMMANDS list is executed. Otherwise, each elif COMMANDS list is
executed in turn, and if its exit status is zero, the corresponding
then COMMANDS list is executed and the if command completes. Otherwise,
the else COMMANDS list is executed, if present. The exit status of the
entire construct is the exit status of the last command executed, or zero
if no condition tested true.

Exit Status:

Returns the status of the last command executed.

bash function return in if statement not working

The simplest, directest answer is to just create functions that consist only of the tests themselves:

INPUT=$1

divisibleBy4() {
[ $(($INPUT % 4)) -eq 0 ]
}

notDivisibleBy100() {
[ $(($INPUT % 100)) -ne 0 ]
}

divisibleBy400() {
[ $(($INPUT % 400)) -eq 0 ]
}

The reason this works is that a function without a return will implicitly return the status of the last command in the function; in these cases, that's the test command (note: [ is a command, even though it doesn't look like one), so the functions just return the result of the test directly.

I'd make at least one change to these, though: they all test the value of the shell variable INPUT; it's much better practice to actually pass the data that functions operate on as parameters. Thus, it'd be better to do something like this:

divisibleBy4() {
[ $(($1 % 4)) -eq 0 ]
}

if divisibleBy4 "$1" ...

Rather than this:

divisibleBy4() {
[ $(($INPUT % 4)) -eq 0 ]
}

INPUT=$1
if divisibleBy4 ...

Note that you can also bundle up the whole leap year check the same way:

isLeapYear() {
[ $(($1 % 4)) -eq 0 ] && [ $(($1 % 100)) -ne 0 ] || [ $(($1 % 400)) -eq 0 ]
}

if isLeapYear "$1"; then

Or use the simpler form @Wiimm suggested:

isLeapYear() {
(( !($1%4) && $1%100 || !($1%400) ))
}

Also, for the shell variables you do use, lower- or mixed-case is preferred, to avoid accidental conflicts with the many all-caps variable names that have special meanings or functions.

BASH : Calling functions from IF statement and comparing the return value. Doing this for multiple functions

When you use $(command), it's replaced with the standard output of the command, not its exit status. Since your function doesn't produce any output, it will never be equal to "0".

You don't need [[ to test the exit status, the if command does that by itself.

if file_exists /opt/file1 && file_exists /tmp/file2
then
# next steps here
else
echo "Some files missing"
fi

How to use bash return code in conditional?

The return code is available in the special parameter $? after the command exits. Typically, you only need to use it when you want to save its value before running another command:

valid_ip "$IP1"
status1=$?
valid_ip "$IP2"
if [ $status1 -eq 0 ] || [ $? -eq 0 ]; then

or if you need to distinguish between various non-zero statuses:

valid_ip "$IP"
case $? in
1) echo valid_IP failed because of foo ;;
2) echo valid_IP failed because of bar ;;
0) echo Success ;;
esac

Otherwise, you let the various operators check it implicitly:

if valid_ip "$IP"; then
echo "OK"
fi

valid_IP "$IP" && echo "OK"

Here is a simple, idiomatic way of writing valid_ip:

valid_ip () {
local ip=$1
[[ $ip =~ ^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$ ]] && {
IFS='.' read a b c d <<< "$ip"
(( a < 255 && b < 255 && c < 255 && d << 255 ))
}
}

There are two expressions, the [[...]] and the { ... }; the two are joined by &&. If the first fails, then valid_ip fails. If it suceeds, then the second expression (the compound statement) is evaluated. The read splits the string into four variables, and each is tested separately inside the arithmetic expression. If all are true, then the ((...)) succeeds, which means the && list succeeds, which means that valid_ip succeeds. No need to store or return explicit return codes.

How to compare result of function in if statement using idiomatic bash

Bash functions return exit codes back to the shell which can be retrieved from the value of $? as in your first case. You need to read through Return value in a Bash function to understand the difference between exit code and return value from function.

As explained in detail in multiple answers including Function return values within BASH if statements you can directly use the function invocation in the if statement. A return code of 0 from the function would assert the if condition to true. So you can do something like

if vercomp "$prev" "$new"; then
printf 'return code zero \n'
else
case $? in
1) printf 'return code one \n' ;;
2) printf 'return code two \n' ;;
*) printf "unknown return code $?\n" ;;
esac
fi

As for the failure cases

  1. This result=vercomp $prev $new is not even a valid function execution in bash but an incorrect variable assignment. To run function and store the output in a variable, you need to use Command Substitution $(..) You need to have written it as result=$(vercomp "$prev" "$new")
  2. And if [[ vercomp $prev $new -eq 1 ]]; then again is an incorrect invocation of the function. Everything inside [[..]] is evaluated in a string context. So the test operator [[ just treats it as incorrect operation and would have thrown a parser error
  3. [[ $(vercomp $prev $new) -eq 1 ]] is also incorrect. All commands in bash return an exit code to the shell the program is invoked from. The command substitution inherently returns the code to the shell via $? but the -eq operator checks for a string value returned from the invocation. Since the construct does not put any contents to stdout, there won't anything to match with 1 or 2. If you done soemthing like [[ $(vercomp "$prev" "$new"; printf "$?") -eq 1 ]] it would have worked, but again not a good practice.

In bash, can you use a function call as a condition in an if statement?

You don't use [[ (or [) when running a command and checking the result code.

if f1 && f2 ; then
echo "success"
else
echo "fail"
fi

Return value in a Bash function

Although Bash has a return statement, the only thing you can specify with it is the function's own exit status (a value between 0 and 255, 0 meaning "success"). So return is not what you want.

You might want to convert your return statement to an echo statement - that way your function output could be captured using $() braces, which seems to be exactly what you want.

Here is an example:

function fun1(){
echo 34
}

function fun2(){
local res=$(fun1)
echo $res
}

Another way to get the return value (if you just want to return an integer 0-255) is $?.

function fun1(){
return 34
}

function fun2(){
fun1
local res=$?
echo $res
}

Also, note that you can use the return value to use Boolean logic - like fun1 || fun2 will only run fun2 if fun1 returns a non-0 value. The default return value is the exit value of the last statement executed within the function.

Bash conditional mixing function return value and conditional check

Use && to do a logical-AND of the result of multiple commands:

if [ -z "$foo" ] && foo "$myparam1" "$myparam2"; then

[ is a command[1]. foo is a different command. [ ... && foo ... (written thus because ] is just an argument to the [ command, not any kind of special syntax) is a compound command that has a truthy result only if both [ and foo both have truthy results.


[1] - Your shell is likely to have a built-in implementation as a performance optimization rather than relying on /bin/[ or /usr/bin/[, but at a language level it's parsed the same as anything else.

Returning value from called function in a shell script

A Bash function can't return a string directly like you want it to. You can do three things:

  1. Echo a string
  2. Return an exit status, which is a number, not a string
  3. Share a variable

This is also true for some other shells.

Here's how to do each of those options:

1. Echo strings

lockdir="somedir"
testlock(){
retval=""
if mkdir "$lockdir"
then # Directory did not exist, but it was created successfully
echo >&2 "successfully acquired lock: $lockdir"
retval="true"
else
echo >&2 "cannot acquire lock, giving up on $lockdir"
retval="false"
fi
echo "$retval"
}

retval=$( testlock )
if [ "$retval" == "true" ]
then
echo "directory not created"
else
echo "directory already created"
fi

2. Return exit status

lockdir="somedir"
testlock(){
if mkdir "$lockdir"
then # Directory did not exist, but was created successfully
echo >&2 "successfully acquired lock: $lockdir"
retval=0
else
echo >&2 "cannot acquire lock, giving up on $lockdir"
retval=1
fi
return "$retval"
}

testlock
retval=$?
if [ "$retval" == 0 ]
then
echo "directory not created"
else
echo "directory already created"
fi

3. Share variable

lockdir="somedir"
retval=-1
testlock(){
if mkdir "$lockdir"
then # Directory did not exist, but it was created successfully
echo >&2 "successfully acquired lock: $lockdir"
retval=0
else
echo >&2 "cannot acquire lock, giving up on $lockdir"
retval=1
fi
}

testlock
if [ "$retval" == 0 ]
then
echo "directory not created"
else
echo "directory already created"
fi


Related Topics



Leave a reply



Submit