Returning a boolean from a Bash function

前端 未结 10 966
余生分开走
余生分开走 2020-12-07 07:02

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 retur

相关标签:
10条回答
  • 2020-12-07 07:51

    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
    }
    
    0 讨论(0)
  • 2020-12-07 07:53

    Why you should care what I say in spite of there being a 250+ 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).
    4. It requires (or will encourage) extraneous and ridiculous comments.
    5. 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

    So, while @Kambus demonstrated that with such a simple function, no return is needed at all. I think 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 exist 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 $(...) 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.

    0 讨论(0)
  • 2020-12-07 07:54

    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... !

    0 讨论(0)
  • 2020-12-07 08:02

    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
    
    0 讨论(0)
提交回复
热议问题