Returning a boolean from a Bash function
Solution 1
Use 0 for true and 1 for false.
Sample:
#!/bin/bash
isdirectory() {
if [ -d "$1" ]
then
# 0 = true
return 0
else
# 1 = false
return 1
fi
}
if isdirectory $1; then echo "is directory"; else echo "nopes"; fi
Edit
From @amichair's comment, these are also possible
isdirectory() {
if [ -d "$1" ]
then
true
else
false
fi
}
isdirectory() {
[ -d "$1" ]
}
Solution 2
Why you should care what I say in spite of there being a 350+ upvote answer
It's not that 0 = true
and 1 = false
. It is: zero means no failure (success) and non-zero means failure (of type N).
While the selected answer is technically "true" please do not put return 1
** in your code for false. It will have several unfortunate side effects.
- Experienced developers will spot you as an amateur (for the reason below).
- Experienced developers don't do this (for all the reasons below).
- It is error prone.
- Even experienced developers can mistake 0 and 1 as false and true respectively (for the reason above).
- It requires (or will encourage) extraneous and ridiculous comments.
- It's actually less helpful than implicit return statuses.
Learn some bash
The bash manual says (emphasis mine)
return [n]
Cause a shell function to stop executing and return the value n to its caller. If n is not supplied, the return value is the exit status of the last command executed in the function.
Therefore, we don't have to EVER use 0 and 1 to indicate True and False. The fact that they do so is essentially trivial knowledge useful only for debugging code, interview questions, and blowing the minds of newbies.
The bash manual also says
otherwise the function’s return status is the exit status of the last command executed
The bash manual also says
($?) Expands to the exit status of the most recently executed foreground pipeline.
Whoa, wait. Pipeline? Let's turn to the bash manual one more time.
A pipeline is a sequence of one or more commands separated by one of the control operators ‘|’ or ‘|&’.
Yes. They said 1 command is a pipeline. Therefore, all 3 of those quotes are saying the same thing.
-
$?
tells you what happened last. - It bubbles up.
My answer
While @Kambus demonstrated that with such a simple function, no return
is needed at all. I think it
was unrealistically simple compared to the needs of most people who will read this.
Why return
?
If a function is going to return its last command's exit status, why use return
at all? Because it causes a function to stop executing.
Stop execution under multiple conditions
01 function i_should(){
02 uname="$(uname -a)"
03
04 [[ "$uname" =~ Darwin ]] && return
05
06 if [[ "$uname" =~ Ubuntu ]]; then
07 release="$(lsb_release -a)"
08 [[ "$release" =~ LTS ]]
09 return
10 fi
11
12 false
13 }
14
15 function do_it(){
16 echo "Hello, old friend."
17 }
18
19 if i_should; then
20 do_it
21 fi
What we have here is...
Line 04
is an explicit[-ish] return true because the RHS of &&
only gets executed if the LHS was true
Line 09
returns either true or false matching the status of line 08
Line 13
returns false because of line 12
(Yes, this can be golfed down, but the entire example is contrived.)
Another common pattern
# Instead of doing this...
some_command
if [[ $? -eq 1 ]]; then
echo "some_command failed"
fi
# Do this...
some_command
status=$?
if ! (exit $status); then
echo "some_command failed"
fi
Notice how setting a status
variable demystifies the meaning of $?
. (Of course you know what $?
means, but someone less knowledgeable than you will have to Google it some day. Unless your code is doing high frequency trading, show some love, set the variable.) But the real take-away is that "if not exit status" or conversely "if exit status" can be read out loud and explain their meaning. However, that last one may be a bit too ambitious because seeing the word exit
might make you think it is exiting the script, when in reality it is exiting the (...)
command group subshell.
** If you absolutely insist on using return 1
for false, I suggest you at least use return 255
instead. This will cause your future self, or any other developer who must maintain your code to question "why is that 255?" Then they will at least be paying attention and have a better chance of avoiding a mistake.
Solution 3
myfun(){
[ -d "$1" ]
}
if myfun "path"; then
echo yes
fi
# or
myfun "path" && echo yes
Solution 4
Be careful when checking directory only with option -d !
if variable $1 is empty the check will still be successfull. To be sure, check also that the variable is not empty.
#! /bin/bash
is_directory(){
if [[ -d $1 ]] && [[ -n $1 ]] ; then
return 0
else
return 1
fi
}
#Test
if is_directory $1 ; then
echo "Directory exist"
else
echo "Directory does not exist!"
fi
Solution 5
Use the true
or false
commands immediately before your return
, then return
with no parameters. The return
will automatically use the value of your last command.
Providing arguments to return
is inconsistent, type specific and prone to error if you are not using 1 or 0. And as previous comments have stated, using 1 or 0 here is not the right way to approach this function.
#!/bin/bash
function test_for_cat {
if [ "$1" = "cat" ];
then
true
return
else
false
return
fi
}
for i in cat hat;
do
echo "${i}:"
if test_for_cat "${i}";
then
echo "- True"
else
echo "- False"
fi
done
Output:
$ bash bash_return.sh
cat:
- True
hat:
- False
Related videos on Youtube
luca
Updated on April 25, 2022Comments
-
luca almost 2 years
I want to write a bash function that check if a file has certain properties and returns true or false. Then I can use it in my scripts in the "if". But what should I return?
function myfun(){ ... return 0; else return 1; fi;}
then I use it like this:
if myfun filename.txt; then ...
of course this doesn't work. How can this be accomplished?
-
glenn jackman about 13 yearsdrop the
function
keyword,myfun() {...}
suffices -
Eelvex about 13 yearsWhat matters to
if
is the zero-exit status ofmyfun
: ifmyfun
exits with0
,then ...
is executed; if it is anything elseelse ...
is executed. -
Gordon Davisson almost 11 years@nhed: the
function
keyword is a bashism, and will cause syntax errors in some other shells. Basically, it's either unnecessary or forbidden, so why use it? It's not even useful as a grep target, since it might not be there (grep for()
instead). -
nhed almost 11 years@GordonDavisson: what? there are other shells? ;-)
-
Bruno Bronosky over 6 yearsPlease don't use 0 and 1. See stackoverflow.com/a/43840545/117471
-
-
luca about 13 yearsmmm.. but then I'll have to use the [ ] operator? Or it should work like that? Maybe then I'm doing something else wrong...
-
Erik about 13 yearsNo you don't need to do that - see the sample.
-
Dewsworld almost 12 years@Erik can you please explain about that
$1
on the if. I didn't understand how it works without[]
and it doesn't show any value even. -
amichair about 11 yearsFor better readability you can use the 'true' command (which does nothing and completes successfully, i.e. returns 0) and 'false' command (which does nothing and completes unsuccessfully, i.e. returns a non-zero value). Also, a function that ends without an explicit return statement returns the exit code of the last executed command, so in the example above, the function body can be reduced to only
[ -d "$1" ]
. -
DRaehal about 10 yearsI am uncertain as to how this answers the question asked. While it is nice to know that an empty $1 can return a true when empty, it does not provide any insight into how to return true or false from a bash function. I would suggest creating a new question "What happens when you do a test on an empty shell variable?" And then posting this as the answer.
-
flying sheep about 10 yearsBengt: it makes sense wheen you think of it as “error code”: error code 0 = everything went ok = 0 errors; error code 1 = the main thing this call was supposed to do failed; else: fail! look it up in the manpage.
-
demonkoryu about 8 yearsYeah, but he probably returned 1 for true and 0 for error.
-
morgents almost 8 yearsNote, that if you add proper quoting to
$1
("$1"
) then you don't need to check for an empty variable. The[[ -d "$1" ]]
would fail because this""
is not a directory. -
einpoklum over 7 yearsWhat about negation?
-
Davos over 6 yearsIt makes sense when you consider that in programming things can usually only succeed in one way, but can fail in infinite ways. Well maybe not infinite, but lots, the odds are stacked against us. Success/Error(s) is not boolean. I think this "Use 0 for true and 1 for false." should read "Use 0 for success and non-zero for failure".
-
Bruno Bronosky over 6 yearsPlease don't use 0 and 1. See stackoverflow.com/a/43840545/117471
-
Mark Reed over 6 yearsWhat about it, @einpoklum?
-
einpoklum over 6 years@MarkReed: I meant, add the "else" case to your example.
-
ZeroPhase over 6 yearsI could have sworn there are languages where the c paradigm of 1 and 0 for true / false are reversed. Though, you should probably make a macro or whatever the language equivalent is for those.
-
Bruno Bronosky over 6 years@ZeroPhase 1 & 0 for false & true would be ridiculous. If you have a binary datatype, there is no reason for that. What you are dealing with in bash is a status code that reflects success (singular) and failures (plural). It's "
if
success do this,else
do that." Success at what? Could be checking for true/false, could be checking for a string, integer, file, directory, write permissions, glob, regex, grep, or any other command that is subject to failures. -
Bruno Bronosky over 6 years@some you can upvote my comment on the selected answer so that more people see this one. ;-) stackoverflow.com/questions/5431909/…
-
Beejor over 6 yearsAvoiding the standard use of 0/1 as a return value just because it's obtuse and prone to confusion is silly. The entire shell language is obtuse and prone to confusion. Bash itself uses the 0/1 = true/false convention in its own
true
andfalse
commands. That is, the keywordtrue
literally evaluates to a 0 status code. In addition, if-then statements by nature operate on booleans, not success codes. If an error happens during a boolean operation, it should not return true or false but simply break execution. Otherwise you get false positives (pun). -
Bruno Bronosky over 6 years@Beejor in your first sentence it sounds like you are saying it's not worth making things better because it's not possible to make them perfect. To that I say en.wikipedia.org/wiki/Perfect_is_the_enemy_of_good To the rest of your claims, it sounds like you either didn't read/understand my post, or gave up on bash and didn't learn it. "if-then statements by nature operate on booleans" Erm, wat? "success codes" Wat? Sounds like you started with a pun you wanted to make and worked backward. If you gave me an opportunity to help you and I missed it, please clarify.
-
Beejor over 6 years@BrunoBronosky What I meant is that working around 0/1 is not really better per se, since it's the convention Bash uses (how
true
andfalse
work), even though the de facto convention in programming is 0=false. Bash mixes the concept of return/status/success/exit/whatever codes and booleans in an odd manner, as does your sanctimonious diatribe. So what I'm saying is more akin to "if it ain't broke", or rather "if it's broke, why fix it and break it another way". -
Beejor over 6 years@BrunoBronosky The general concept of if-then across all programming languages is such that "if (statement evaluating to true or false)" (binary) and not "if (statement evaluating to integer, representing how the function failed or not)" (n-ary). Forcing a return code of 0/1 ensures a predictable binary result when you need one. It's not for every case, but works fine for creating functions to use with
if
. Either way, tomayto tomahto. I apologize if you took my post to heart, or I used the wrong nomenclature, but I use Bash a lot, and there's no need to talk down to me. -
Bruno Bronosky over 6 years@Beejor You refuse to let go of your flawed thinking. Bash DOES NOT use 0/1 for True/False values. It uses zero and non-zero for exit codes, the latter meaning failure. You know this but you are obstinate. The command
false
exits with [only] 1 (aka 2^0) because it has exactly 1 failure mode. If it had multiple it would use a power of 2 for each and bitwise AND these values together any time it needed to exit with multiple codes. A single exit code can give you countless amounts of information. It's brilliant, though a little confusing at first. -
Bruno Bronosky over 6 years@Beejor bash does not mix the concept of exit codes and booleans. You do. Bash is untyped, but even using declare, boolean is not an option. MANY languages perform
if
on "non binary" types. It's called truthiness. In bash, 0 is truthy. This is how I #DealWithIt. "Forcing a return code of 0/1" gives you nothing that using 0/255 ortrue
/false
lacks… except reader confusion. I'll stop there because I respect stackoverflow.com/a/35697810/117471 -
Craig Hicks about 6 years" if ! $(exit $status); then " -- That deserves a better explanation. It's not intuitive. I have to think about whether the program will exit before printing the message, or not. The rest of your answer was good, but that spoiled it.
-
Bruno Bronosky about 6 years@CraigHicks, valid point. I added a sentence to address it. Honestly, I was confused when I first looked at it. At 5am it was not immediately obvious to me that it was the subshell that was being exited. When I wrote it, I had been writing bash for hours and was in the zone.
-
Hrobky about 6 yearsmyfun "path" || echo no
-
JepZ over 5 years@BrunoBronosky I read your answer and I think I understand the distinction between the content pipeline (stdin->stdout) and the error codes in return values. Yet, I don't understand why using
return 1
should be avoided. Assuming I have avalidate
function I find it reasonable toreturn 1
if the validation fails. After all, that is a reason to stop the execution of a script if it isn't handled properly (e.g., when usingset -e
). -
Bruno Bronosky over 5 years@Jepz that’s a great question. You are correct that
return 1
is valid. My concern is all the comments here saying “0=true 1=false is [insert negative word]”. Those people are likely to read your code some day. So for them seeingreturn 255
(or 42 or even 2) should help them think about it more and not mistake it as “true”.set -e
will still catch it. -
Jesse Chisholm over 4 yearsre:
mixing of booleans and exit codes
If you are going down that road, then it isn't justbash
but most oflinux
and thec/c++
programs aimed atlinux
. Numerous program suites have something that equates to#define NO_ERROR 0
or#define SUCCESS 0
. -
Akito about 4 yearsThere is nothing wrong with using
return 1
... Especially if you do not want to reference an earlier command. So you are not automatically an "amateur" doing that... -
BuvinJ almost 4 yearsExecuting
true
orfalse
inside a sub shell changes the outer scope? That makes some sense, considering that executing a failed binary would definitely trigger a "fail state". Are you sure that using true in this way "clears" it? What happens if you executefalse
prior to returning$(true)
? -
BuvinJ over 3 yearsI tested my concern, and it was not a problem. I can confirm this syntax works as expected, at least in Dash. Nice!
-
BuvinJ over 3 yearsThe downside to this, however, is that you spawn an entire sub process (in many interpreters anyway) in order to simply return a primitive. The inefficiency of that, compared to any other "real" language, is staggering! I understand that it is very difficult to avoid such in shell scripting, but it is possible. In fact, I developed a library which allows one to avoid such insanity, generally speaking, and seen it dramatically speed up some "heavy" scripts. While my "backwards" answer is a bit less readable, I would still use it vs this pretty one if I cared about the processing details.
-
BuvinJ over 3 yearsTo be picky (then I'll drop it...) this not "one command" btw. It's still two. One is nested inside a sub shell.
-
RandomName over 3 yearsIf you're building a library and want to cater for "heavy" scripts then declaring
TRUE=0; FALSE=1
somewhere andreturn $TRUE
orreturn $FALSE
would be a better answer. Code readability is more important than slight performance gains in most cases to use bash scripts. If performance and optimization is needed then bash isn't the answer. -
BuvinJ over 3 yearsSee the answer by Bruno Bronosky regarding a return of 0 or 1...
-
BuvinJ over 3 yearsTo be clear, I agree that your syntax is nicer to read, that readability generally trumps performance, and that shell scripting is the wrong language if you're goal is performance. That said, there are absolutely valid use cases where you would want good performance from such, and where such optimizations go a very long way.
-
BuvinJ over 3 yearsFor instance, I wrote a more robust emulation of the python "os.walk", which recursively traces through file system trees and fires off "call back" functions for you to process the files, directories, sym links etc. It's super useful in shell scripting. It has a good deal work to do, and leans on other custom functions. When processing large volumes of data, my optimizations to avoid spawning a billion sub processes has a massive impact.
-
RandomName over 3 yearsBruno Bronosky's answer against returning 0 or 1 is also for code readability reasons. Returning
$TRUE
or$FALSE
fixes those concerns and without the subshells used in my answer. No code comments needed, looks clean and obvious what the developer was thinking. -
BuvinJ over 3 yearsI've written scripts like that many times. The thing is, if you write the rest of a script in a sophistated style, requiring the reader to have an advanced understanding for shell scripting to begin with, using the builtin statements
true
andfalse
as designed and intended is not exactly writing in an unreadable manner. -
BuvinJ over 3 yearsHere's a little example of what I mean. What if I want to write
[ -d "${path}" ] && rm -R "${path}"
That's ninja style bash, rather than an if/then. Should I not write that, because it's funky as all hell compared to how to one would write that in a more readable language? It's my general observation that in shell scripting (and in an interesting parallel Batch...) that you are basically funnel into writing less and less readable code as your skills advance. This is true in a sense for any language, but it's more so syntactically in these terminal/shell input contexts. -
RandomName over 3 yearsThere is no right or wrong answer but I believe that we should try to be consistent from language to language if possible. The voice in my head reads
[ -d "${path}" ] && rm -R
asif directory exists then remove it recursively.
Writing it with an if/then statement makes it quicker for our human brains to understand, also, makes it easier to skim past those statements when looking for some other piece of code. So yes, I would preferif [ -d "${path}" ]; then rm --recursive "${path}"; fi
. -
david valentino over 3 yearsnice, neat and understandable at first glance
-
zaTricky over 3 years"exit" or "exist" ? It's ambiguous and I find it strange nobody has commented on this after 3 years. :-|
-
yurez over 3 yearsWhat if run my script with
bash -e
? Will it terminate in the middle when I usefalse
? -
Martin Kealey over 2 yearsThe
$(
inif ! $(exit $status) ; then
is problematic, because it relies on the part inside the parentheses not outputting anything - which is true for a simpleexit
, but not necessarily true if it's more complicated. Perhaps you meantif ! ( exit "$status" ) ; then
? Or even better,if ! some_command ; then ...
? -
Martin Kealey over 2 yearsPerhaps improve this code so that it doesn't spit out weird errors when you call it with
if test_for_cat 'snow leopard' ; then echo "It's a feline" ; else echo "It's something else" ; fi
-
WinEunuuchs2Unix about 2 yearsOn February 19, 2022 the "250+" (upvotes) can be changed to "350+" or, perhaps reworded altogether.
-
Bruno Bronosky about 2 years@MartinKealey I fixed the quoting that caused your error. But this is another example of why you should always use
[[
instead of[
unless you absolutely must be POSIXsh
compliant. See: mywiki.wooledge.org/BashFAQ/031 -
Martin Kealey about 2 years@morgents inside
[[
...]]
the quotes make no difference. Inside[
...]
it does of course make a difference, because if the variable expansion is empty and elided, that just leaves[ -d ]
, which simply tests whether-d
is a non-empty string; that of course is true. -
Martin Kealey about 2 years(And from a wider perspective, some Unices will treat an empty filepath as equivalent to
.
, and some won't.) -
Martin Kealey about 2 yearsI dislike this use of
$(true)
and$(false)
because it breaks another important stylistic rule: always quote expansions. Someday someone is going to through the ShellCheck results, and "fix" this code by changingreturn $(true)
toreturn "$(true)"
. Which isn't a syntax error, but will then blow up asreturn
complainsnumeric argument required
since an empty string is not a number. -
Martin Kealey about 2 yearsIf that
test
function had contained aprintf
, you would have noticed the lack of output from[ test ]
. The rule is quite simple: the[
is not part of the inherent syntax of theif
structure; rather it takes a list of commands, and[
is a (strangely named) command. By the way, calling a functiontest
is a bad idea, sincetest
is a built-in (and equivalent to[
). -
Martin Kealey about 2 yearsAny time I see
return 255
, I'm more inclined to think "some newbie mistakenly wrotereturn -1
orexit -1
(thinking it's like a syscall with errno) , and then the effect of that got codified into some internal document, and everyone else kept mimicking it. -
Martin Kealey about 2 years99% of the uses of
$?
are pointless verbosity. If you don't care about anything other than zero/non-zero, then you don't need to look at$?
at all. Instead ofsomecmd ; status=$? ; if (exit "$status") ; then ...
you should simply writeif somecmd ; then ...
. If you really need to record $? (say, to put it in a debugging message) then do that as the first thing in theelse
block (you don't need to in thethen
block, because you know it's0
). -
RandomName about 2 yearsI went with the
return $TRUE
approach myself because it doesn't create subshells. So, because I value code readability way too much, I vow to never usereturn "$TRUE"
just to satisfy some online tool.