299

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?

Vadim Kotov
  • 8,084
  • 8
  • 48
  • 62
luca
  • 12,311
  • 15
  • 70
  • 103
  • 4
    drop the `function` keyword, `myfun() {...}` suffices – glenn jackman Mar 25 '11 at 13:33
  • 2
    What matters to `if` is the zero-exit status of `myfun`: if `myfun` exits with `0`, `then ...` is executed; if it is *anything else* `else ...` is executed. – Eelvex Mar 25 '11 at 18:26
  • 8
    @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). – Gordon Davisson Jun 01 '13 at 07:33
  • 6
    @GordonDavisson: what? there are other shells? ;-) – nhed Jun 01 '13 at 14:41
  • Please don't use 0 and 1. See https://stackoverflow.com/a/43840545/117471 – Bruno Bronosky Sep 06 '17 at 22:05

11 Answers11

418

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" ]
}
Sanghyun Lee
  • 21,644
  • 19
  • 100
  • 126
Erik
  • 88,732
  • 13
  • 198
  • 189
  • mmm.. but then I'll have to use the [ ] operator? Or it should work like that? Maybe then I'm doing something else wrong... – luca Mar 25 '11 at 11:44
  • 4
    No you don't need to do that - see the sample. – Erik Mar 25 '11 at 11:44
  • @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. – Dewsworld May 17 '12 at 04:59
  • 66
    For 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" ]`. – amichair Mar 02 '13 at 18:47
  • 26
    Bengt: 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. – flying sheep Mar 03 '14 at 19:51
  • Yeah, but he probably returned 1 for true and 0 for error. – demonkoryu Apr 15 '16 at 22:20
  • 8
    It 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". – Davos Aug 10 '17 at 03:57
  • 12
    Please don't use 0 and 1. See https://stackoverflow.com/a/43840545/117471 – Bruno Bronosky Sep 06 '17 at 22:05
  • What if run my script with `bash -e`? Will it terminate in the middle when I use `false`? – yurez Dec 01 '20 at 16:06
290

Why you should care what I say in spite of there being a much higher 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.

  1. Experienced developers will spot you as an amateur (for the reason below).
  2. Experienced developers don't do this (for all the reasons below).
  3. It is error prone.
  • Even experienced developers can mistake 0 and 1 as false and true respectively (for the reason above).
  1. It requires (or will encourage) extraneous and ridiculous comments.
  2. 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 the "1=true, 0=false" mistake.

Bruno Bronosky
  • 66,273
  • 12
  • 162
  • 149
  • I 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. – ZeroPhase Sep 16 '17 at 20:54
  • 1
    @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 Sep 18 '17 at 14:56
  • 1
    @some you can upvote my comment on the selected answer so that more people see this one. ;-) https://stackoverflow.com/questions/5431909/returning-a-boolean-from-a-bash-function/43840545?noredirect=1#comment79132096_5431932 – Bruno Bronosky Oct 03 '17 at 16:21
  • 11
    Avoiding 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` and `false` commands. That is, the keyword `true` 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). – Beejor Dec 18 '17 at 21:26
  • 1
    @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 https://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. – Bruno Bronosky Dec 18 '17 at 22:10
  • 1
    @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` and `false` 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 Dec 20 '17 at 23:28
  • 1
    @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. – Beejor Dec 20 '17 at 23:29
  • @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 Dec 21 '17 at 09:50
  • @Beejor bash does not mix the concept of exit codes and booleans. You do. Bash is [untyped](http://tldp.org/LDP/abs/html/untyped.html), but even using [declare](http://tldp.org/LDP/abs/html/declareref.html), boolean is not an option. MANY languages perform `if` on ["non binary" types](https://gist.github.com/jfarmer/2647362). 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 or `true`/`false` lacks… except reader confusion. I'll stop there because I respect https://stackoverflow.com/a/35697810/117471 – Bruno Bronosky Dec 21 '17 at 10:03
  • 3
    " 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. – Craig Hicks Mar 06 '18 at 03:44
  • @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. – Bruno Bronosky Mar 06 '18 at 10:39
  • 2
    @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 a `validate` function I find it reasonable to `return 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 using `set -e`). – JepZ Nov 04 '18 at 14:46
  • 1
    @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 seeing `return 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. – Bruno Bronosky Nov 05 '18 at 15:28
  • re: `mixing of booleans and exit codes` If you are going down that road, then it isn't just `bash` but most of `linux` and the `c/c++` programs aimed at `linux`. Numerous program suites have something that equates to `#define NO_ERROR 0` or `#define SUCCESS 0`. – Jesse Chisholm Nov 12 '19 at 18:15
  • 2
    There 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... – Akito Mar 03 '20 at 19:05
  • 2
    "exit" or "exist" ? It's ambiguous and I find it strange nobody has commented on this after 3 years. :-| – zaTricky Oct 19 '20 at 09:51
  • 1
    The `$(` in `if ! $(exit $status) ; then` is problematic, because it relies on the part inside the parentheses not outputting anything - which is true for a simple `exit`, but not necessarily true if it's more complicated. Perhaps you meant `if ! ( exit "$status" ) ; then`? Or even better, `if ! some_command ; then ...`? – Martin Kealey Jan 05 '22 at 10:03
  • On February 19, 2022 the **"250+"** (upvotes) can be changed to **"350+"** or, perhaps reworded altogether. – WinEunuuchs2Unix Feb 19 '22 at 16:55
  • Any time I see `return 255`, I'm more inclined to think "some newbie mistakenly wrote `return -1` or `exit -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 Apr 04 '22 at 01:47
  • 1
    99% 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 of `somecmd ; status=$? ; if (exit "$status") ; then ...` you should simply write `if somecmd ; then ...`. If you _really_ need to record $? (say, to put it in a debugging message) then do that as the first thing in the `else` block (you don't need to in the `then` block, because you know it's `0`). – Martin Kealey Apr 04 '22 at 01:48
  • Would it be wrong to edit the post's 'up vote' claim to reflect the apparently lower value currently attached to the post? – Lee Goddard Feb 17 '23 at 13:24
  • Regarding the current version of the *Another common pattern* code: assuming the script runs with the often-used [`set -e` option](https://www.gnu.org/software/bash/manual/html_node/The-Set-Builtin.html), if `some_command` returns with a nonzero exit status, the whole script script immediately exits. – Abdull Jul 19 '23 at 18:40
40
myfun(){
    [ -d "$1" ]
}
if myfun "path"; then
    echo yes
fi
# or
myfun "path" && echo yes
Bengt
  • 14,011
  • 7
  • 48
  • 66
Kambus
  • 3,504
  • 1
  • 17
  • 10
14

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
RenRen
  • 10,550
  • 4
  • 37
  • 39
  • 1
    I 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. – DRaehal Feb 17 '14 at 15:48
  • 3
    Note, 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. – morgents May 20 '16 at 07:49
  • @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 Apr 03 '22 at 23:08
  • (And from a wider perspective, some Unices will treat an empty filepath as equivalent to `.`, and some won't.) – Martin Kealey Apr 03 '22 at 23:08
12

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
Bruno Bronosky
  • 66,273
  • 12
  • 162
  • 149
mrteatime
  • 129
  • 1
  • 3
  • Perhaps 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` – Martin Kealey Jan 05 '22 at 17:42
  • @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 POSIX `sh` compliant. See: https://mywiki.wooledge.org/BashFAQ/031 – Bruno Bronosky Apr 02 '22 at 00:00
8

For code readability reasons I believe returning true/false should:

  • be on one line
  • be one command
  • be easy to remember
  • mention the keyword return followed by another keyword (true or false)

My solution is return $(true) or return $(false) as shown:

is_directory()
{
    if [ -d "${1}" ]; then
        return $(true)
    else
        return $(false)
    fi
}
LogicalException
  • 566
  • 2
  • 7
  • 16
  • Executing `true` or `false` 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 execute `false` prior to returning `$(true)`? – BuvinJ Jul 19 '20 at 13:30
  • I tested my concern, and it was not a problem. I can confirm this syntax works as expected, at least in Dash. Nice! – BuvinJ Jul 20 '20 at 16:50
  • The 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 Jul 20 '20 at 16:59
  • To be picky (then I'll drop it...) this not "one command" btw. It's still two. One is nested inside a sub shell. – BuvinJ Jul 20 '20 at 19:59
  • 1
    If you're building a library and want to cater for "heavy" scripts then declaring `TRUE=0; FALSE=1` somewhere and `return $TRUE` or `return $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. – LogicalException Jul 21 '20 at 19:28
  • See the answer by Bruno Bronosky regarding a return of 0 or 1... – BuvinJ Jul 21 '20 at 19:47
  • To 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 Jul 21 '20 at 19:47
  • For 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. – BuvinJ Jul 21 '20 at 19:50
  • Bruno 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. – LogicalException Jul 22 '20 at 21:15
  • I'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` and `false` as designed and intended is not exactly writing in an unreadable manner. – BuvinJ Jul 22 '20 at 22:05
  • Here'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. – BuvinJ Jul 22 '20 at 22:29
  • There 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` as `if 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 prefer `if [ -d "${path}" ]; then rm --recursive "${path}"; fi`. – LogicalException Jul 22 '20 at 22:55
  • I 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 changing `return $(true)` to `return "$(true)"`. Which isn't a syntax error, but will then blow up as `return` complains `numeric argument required` since an empty string is not a number. – Martin Kealey Apr 03 '22 at 23:20
  • I 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 use `return "$TRUE"` just to satisfy some online tool. – LogicalException Apr 05 '22 at 01:00
6

I encountered a point (not explictly yet mentioned?) which I was stumbling over. That is, not how to return the boolean, but rather how to correctly evaluate it!

I was trying to say if [ myfunc ]; then ..., but that's simply wrong. You must not use the brackets! if myfunc; then ... is the way to do it.

As at @Bruno and others reiterated, true and false are commands, not values! That's very important to understanding booleans in shell scripts.

In this post, I explained and demoed using boolean variables: https://stackoverflow.com/a/55174008/3220983 . I strongly suggest checking that out, because it's so closely related.

Here, I'll provide some examples of returning and evaluating booleans from functions:

This:

test(){ false; }                                               
if test; then echo "it is"; fi                                 

Produces no echo output. (i.e. false returns false)

test(){ true; }                                                
if test; then echo "it is"; fi                                 

Produces:

it is                                                        

(i.e. true returns true)

And

test(){ x=1; }                                                
if test; then echo "it is"; fi                                 

Produces:

it is                                                                           

Because 0 (i.e. true) was returned implicitly.

Now, this is what was screwing me up...

test(){ true; }                                                
if [ test ]; then echo "it is"; fi                             

Produces:

it is                                                                           

AND

test(){ false; }                                                
if [ test ]; then echo "it is"; fi                             

ALSO produces:

it is                                                                           

Using the brackets here produced a false positive! (I infer the "outer" command result is 0.)

The major take away from my post is: don't use brackets to evaluate a boolean function (or variable) like you would for a typical equality check e.g. if [ x -eq 1 ]; then... !

BuvinJ
  • 10,221
  • 5
  • 83
  • 96
  • 1
    If that `test` function had contained a `printf`, you would have noticed the lack of output from `[ test ]`. The rule is quite simple: the `[` is _not_ part of the inherent syntax of the `if` structure; rather it takes a list of commands, and `[` is a (strangely named) command. By the way, calling a function `test` is a bad idea, since `test` is a built-in (and equivalent to `[`). – Martin Kealey Apr 04 '22 at 01:35
5

Following up on @Bruno Bronosky and @mrteatime, I offer the suggestion that you just write your boolean return "backwards". This is what I mean:

foo()
{
    if [ "$1" == "bar" ]; then
        true; return
    else
        false; return
    fi;
}

That eliminates the ugly two line requirement for every return statement.

BuvinJ
  • 10,221
  • 5
  • 83
  • 96
4

I found the shortest form to test the function output is simply

do_something() {
    [[ -e $1 ]] # e.g. test file exists
}

do_something "myfile.txt" || { echo "File doesn't exist!"; exit 1; }
Roy Shilkrot
  • 3,079
  • 29
  • 25
1

It might work if you rewrite this function myfun(){ ... return 0; else return 1; fi;} as this function myfun(){ ... return; else false; fi;}. That is if false is the last instruction in the function you get false result for whole function but return interrupts function with true result anyway. I believe it's true for my bash interpreter at least.

Nephew
  • 21
  • 3
1

Here are some good examples with test cases to experiment with.

For basic functions, call true/false in the last line of the method, or use true; return to exit early.

function is_true() { true; }

if is_true; then echo 'true is true'; fi
if ! is_true; then exit; else echo '! true is ! true'; fi

function is_false() { false; }

if ! is_false; then echo 'false is false'; fi
if is_false; then exit; else echo '! false is ! false'; fi

If you can not return immediately, store the return value in variable. Use (true; echo $?) while setting the variable. This also works for nested functions (see next section).

function from_var() {
        local input=$1
        local my_var
        if ((input == 1)); then
                my_var=$(true; echo $?)
        else
                my_var=$(false; echo $?)
        fi
        echo 'ignore this line'
        (exit $my_var)
}

if from_var 1; then echo "return true is true"; else exit; fi
if from_var 0; then exit; else echo "return false is false"; fi

If you need to store the result of the function call that returns a bool, use the same technique, but pipe the output of the call to /dev/null or the result may also contain strings from echo or other commands. Notice the (exit $rval) in the if statement lets you correctly interpret the return value. (Other methods like if (($rval)) or if [ $rval ] will not work as expected.

# Return a truthy result
rval=$(from_var 1 >/dev/null; echo $?)
if (exit $rval); then echo "return true as variable is true"; else exit; fi

# Return a falsy result
rval=$(from_var 0 >/dev/null; echo $?)
if (exit $rval); then exit; else echo "return false as variable is false"; fi

The full output of this code is:

true is true
! true is ! true
false is false
! false is ! false
ignore this line
return true is true
ignore this line
return false is false
return true as variable is true
return false as variable is false

If you don't want to suppress the output from within the function using > /dev/null, then rewrite to call the function first.

from_var 0; rval="$?"
cmcginty
  • 113,384
  • 42
  • 163
  • 163