11

I have a function which returns 1 if the number is a valid ten digit number:

valNum()
{
    flag=1
    if [[ $1 != [1-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9] ]]; then
        echo "Invalid Number"
        flag=0
    fi
    return $flag
}

It is getting called by:

if [[ $(valNum $num) -eq 1 ]]; then
      #do something
fi

The function is working fine if the number is valid but is showing syntax error if input a invalid number.

Raphael Ahrens
  • 9,701
  • 5
  • 37
  • 52
user2179293
  • 1,753
  • 5
  • 15
  • 15

4 Answers4

16

@choroba's answer is correct, however this example might be clearer:

valNum $num
valNumResult=$? # '$?' is the return value of the previous command
if [[ $valNumResult -eq 1 ]]
then
  : # do something
fi

This example is a little longer (setting $valNumResult then querying that value), but more-explicitly describes what happens: that valNum() returns a value, and that value can be queried and tested.

P.S. Please do yourself a favor and return 0 for true and non-zero for false. That way you can use the return value to indicate "why we failed" in the failure case.

8

Functions in bash can only return exit codes. The command substitution, conversely, is used to get the standard output of a command or function. Therefore, to check the returned flag, you do not need the substitution:

if valNum "$num" ; then
    # ...
fi

But, for it to work, you should return 0 if the number is valid, and 1 if it is not (exit code 0 means no error).

choroba
  • 45,735
  • 7
  • 84
  • 110
  • I dont get it. In the example 24.7 in http://tldp.org/LDP/abs/html/complexfunct.html the function is returning the max value and not the exit code. Though your suggestion is working but I am not able to understand why it is working – user2179293 Sep 15 '13 at 07:44
  • 1
    since your test is to find out if the input is a valid 10-digit integer or not, i.e. true or false, the function returns either 0 or 1. choroba's example works because `if valnum "$num"` is equivalent to `if valnum "$num" = 0` i.e. "if it is true". basic rule of thumb in sh scripting is that 0 = true/success, non-zero = false/error. – cas Sep 15 '13 at 08:10
  • 2
    BTW, that "Advanced Bash-Scripting Guide" isn't a very good guide - it's wrong about a lot of things and encourages some poor scripting practices. the Bash FAQ at http://mywiki.wooledge.org/BashFAQ is much better resource. – cas Sep 15 '13 at 08:12
  • 1
    see http://unix.stackexchange.com/questions/12236/best-resources-to-learn-bash-scripting – cas Sep 15 '13 at 08:14
  • your function is failing because it is echo-ing the string "Invalid Number", and you then doing a numeric comparison between that string and the number 1 with `if [[ $(valNum $num) -eq 1 ]]` – cas Sep 15 '13 at 08:19
6

You cannot return an arbitrary result from a shell function. You can only return a status code which is an integer between 0 and 255. (While you can pass a larger value to return, it is truncated modulo 256.) The value must be 0 to indicate success and a different value to indicate failure; by convention you should stick to error codes between 1 and 125, as higher values have a special meaning (bad external command for 126 and 127, killed by a signal for higher values).

Since you are returning a yes-or-no result here, a status code is appropriate. Since flag seems to indicate a success or failure, you should use the conventional values of 0 for success and 1 for failure (the opposite of what you wrote). You can then use your function directly in an if statement.

valNum ()
{
  local flag=0
  if [[ $1 != [1-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9] ]]; then
    echo 1>&2 "Invalid Number"
    flag=1
  fi
  return $flag
}
if valNum "$num"; then
  #do something
fi

If you need to discriminate between failure codes, call the function directly. Immediately after it returns, the failure code is available in $?. You can then check it with a case statement:

valNum "$num"
case $? in …

If you need to use the status code later, save it into another variable before $? is overwritten by the next command.

valNum "$num"
valNum_status=$?

What you wrote didn't work because the command substitution $(…) expands to the output of the function, which in your code is either the error message or empty, never 1.

If you need to pass more information than a status code allows out of a shell functions, you have two possibilities:

  • Print some text on standard output, and call the function in a command substitution: $(valNum "$num")
  • Assign to one or more variable inside the function and read those variables later.
Gilles 'SO- stop being evil'
  • 807,993
  • 194
  • 1,674
  • 2,175
2

I've had conflicting results in this area myself. Here are results from my empirical experiments. Firstly, some 'theory' about bash or *nix commands:

  • SUCCESS == 0 ... viz. no error status code)
  • FAIL          != 0 ...... some status code

Example:

if  ls -lt /nonexistantdir
then 
    echo "found"
else
    echo "FAIL"
fi
#
echo
ls -lt /nonexistantdir; echo "status = $?"
echo "status = $?"

Output:

ls: cannot access '/nonexistantdir': No such file or directory
FAIL... 

ls: cannot access '/nonexistantdir': No such file or directory
status = 2

As shown, the ls command returns status code = 2. When you try a valid directory, the status is zero(0). Not the same as almost all other languages.

rule #1 -- Make ...

  • TRUE   == 0
  • FALSE != 0

We must remember that we are testing error codes in a Bash if statement. I set-up constants, or you can use shell true or false commands.

TRUE=0
FALSE=1

#  valid number function
#
valNum()
{
    flag=$TRUE

    if [[ $1 != [1-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9] ]]; then
        echo "Invalid Number"
        flag=$FALSE
    fi
    return $flag
}

#    later on ...
#
if validNum Abc 
then
    echo "Lucky number"
else
    echo "Not lucky."
fi

and output:

Invalid Number
Not lucky.

However, I suggest you give any 'up-vote' @Gilles because his answer is the correct one. I just wanted to get the simplistic-side down on ePaper.

Just one other thing, the test command. This looks like:

[[ some-expression ]]; 

Most of the time. And for example:

$ test 1
$ echo "result = $?"
result = 0
$ test 0
$ echo "result = $?"
result = 0

Zero(0) being true. Why? Well the man page says that a single argument is 'true' when it is NOT-NULL.

references:

will
  • 480
  • 1
  • 6
  • 12