11

I've set a function in a shell script that checks if a folder exists, if it does not exist, tries to created it, and if it can not create the folder (for example if the user does not the right permission) return 1. Then I check this "return", but I don't understand why "if" does not work because the return equal 1.

Code:

#!/bin/bash
# Main folders
INPUT="input"
OUTPUT="output"

# Functions

function checkFolderExist(){
    if [ -d $1 ]
    then
        # 0 = true
        # Change to 0, only for tests.
        return 1
    else
        mkdir $1
        result=$?
        if [ result==0 ]
        then
            # 0 = true
            return 0
        else
            # 1 = false
            return 1
        fi
    fi
}


CHECKINPUT=$(checkFolderExist $INPUT)
echo $?
CHECKOUTPUT=$(checkFolderExist $OUTPUT)
echo $?

# If folders does not exist, exit the script
if [[ "$CHECKINPUT" = 1 || "$CHECKOUTPUT" = 1 ]]; then
    echo "[+] Error. Folder does not exist. Check user permissions."
    exit 1
fi
0

3 Answers 3

22

There's a few things here.

  • You very seldom have to explicitly check $? against anything or save it in a variable (unless you need to reference the same exit status multiple times).
  • The exit status of a function is the exit status of the last executed command in the function, so an explicit return is seldom needed (seldom with an explicit return value at least).
  • A function that checks whether a directory exists should not create any directories. Better call it create_dir_if_needed.
  • There's an error in [ result==0 ]. The string result==0 is a string of non-zero length, and testing a string in this way will return true if the string has non-zero length, so the test is always true. You probably wanted [ "$result" -eq 0 ] instead.
  • Remember to always double quote variable expansions and command substitutions, unless you know in what contexts this is not needed.

With these things in mind:

create_dir_if_needed () {
    mkdir -p -- "$1"
}

This would return the exit status of mkdir -p -- "$1". This command would create the named directory (and any intermediate directories) if this did not already exist. If the mkdir command fails to create the directory, it will exit with a non-zero exit status, which will become the exit status of the function. mkdir -p will not fail if the directory already exists.

You would use this as

if ! create_dir_if_needed "$dirpath"; then
    printf 'Failed to create directory "%s"\n' "$dirpath" >&2
    exit 1
fi

or, since the function is trivial, you could get rid of it and say

if ! mkdir -p -- "$dirpath"; then
    printf 'Failed to create directory "%s"\n' "$dirpath" >&2
    exit 1
fi

A variation of the create_dir_if_needed function that uses mkdir without -p and will therefore never create missing parent directories to the given directory path:

create_dir_if_needed () {
    if [ -d "$1" ]; then
        return
    fi

    mkdir -- "$1"
}

or,

create_dir_if_needed () {
    [ -d "$1" ] || mkdir -- "$1"
}

A call to this function would return true (zero) if the directory already existed or if the mkdir call went well. A return statement with no explicit value will return the exit status of the most recently executed statement, in this case it would return the positive outcome of the [ -d "$1" ] test.

4
  • 1
    Amazing!! Your explanation was incredible. Thanks a lot. One more thing. What does "--" in mkdir means? It is the first time that I see this. Thanks a lot!
    – jask
    Commented Apr 4, 2019 at 10:32
  • 3
    @jask The double dash is a way of saying "there are no further command line options". We use it here because we don't know the value of the variable that we use. If its value starts with a dash, it would be mistaken for a set of command line options. This is not a special thing for mkdir but applies to most command line utilities.
    – Kusalananda
    Commented Apr 4, 2019 at 10:50
  • 1
    Out of curiosity, what would create_dir_if_needed look like with an explicit return? If I simply put return before [ -d "$1" ], I get an error that says return: [: numeric argument required. Commented Jan 15, 2020 at 0:40
  • 1
    @CameronHudson There would be an extra line at the end saying just return or return "$?", but neither of these would be necessary and would only serve to confuse a reader.
    – Kusalananda
    Commented Oct 31, 2022 at 17:33
6

Bash does not work like regular programming languages when it comes to returning values.

Here you are confusing output from checkFolderExist with return status from checkFolderExist.

Your CHECKINPUT and CHECKOUTPUT variables will be empty because your function does not echo nor printf anything.

Should you really want to save your function’s return status for later use you should rather do:

checkFolderExist "${INPUT}"
CHECKINPUT=$?

checkFolderExist "${OUTPUT}"
CHECKOUTPUT=$?

Besides this, I’d anyway recommend you to follow the advices in Kusalananda’s answer that direct you to better techniques to address the problem.

2
  • 1
    Thanks mate!! As you can see I come from a "standard" language programming so, to me, bash is slightly different. Thanks for your explanation.
    – jask
    Commented Apr 4, 2019 at 11:17
  • In the 20 some years I've been using unix,I've never had to figure out how to do this until now:(The utility xmessage by default only returns responses via the return code).
    – jwrush
    Commented Nov 7, 2021 at 1:13
4

You are mixing up output versus return value of a function. I show you minimal working examples, so you can see your mistake:

Either do a return:

myfunc() {
  return 1
}

myfunc
ret=$?
[ $ret -eq 0 ] && echo OK

or write to stdout and check the output:

myfunc() {
  echo '1'
}

ret="$(myfunc)"
[ "$ret" = '0' ] && echo OK

Also note that I prefer distinguishing integer from string of the variable ret, which is not really necessary if you know that the output can only be numeric but makes it cleaner. Furtheron, capturing the return value into a variable is not necessary if the conditional statement follows immediately.

It is the first one you wanted to have, so you must not use $(). You really mixed them: Return value as in the first example, and checking output as in the second example.

0

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .