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 thethen COMMANDS
list is executed. Otherwise, eachelif COMMANDS
list is
executed in turn, and if its exit status is zero, the correspondingthen COMMANDS
list is executed and the if command completes. Otherwise,
theelse 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
- 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 asresult=$(vercomp "$prev" "$new")
- 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 [[ $(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:
- Echo a string
- Return an exit status, which is a number, not a string
- 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
Pipe Tar Extract into Tar Create
Split and Rename The Splitted Files in Shell Script
Whats The Easiest Way to Send Messages to My Linux Daemon App
Code Behind Rstudio Server Export Function
Avoid Copying of Data Between User and Kernel Space and Vice-Versa
How to Get a Process Tree Trace/Log of a Process in Linux
X11 Configurenotify() Always Returning X,Y = (0,0)
Tomcat 6 Log4J - Linux - Safely Remove Catalina.Out
Monitoring (Sniffing) /Dev/Ttyusb0 Created by Ftdi Usb Serial Converter
How Does The Os Know Disk Address of an Absent Page
Arguments Were Passed Wrong in Pthread
Iptables Remove Specific Rules by Comment
How to Do Simple Arithmetic in Sed Addresses
Can Not Add New User in Docker Container with Mounted /Etc/Passwd and /Etc/Shadow