'Detecting negative numbers without interfering with invalid options (starting with -)

I have a bash function that validates numeric values (positive and negative, including integers, floats and in scientific notation).

Using a argument parsing loop, I also want to take care of invalid options (beginning with -, which however interferes with user supplied negative numbers).

 while (( $# > 0 )); do
    opt="$1"
    case $opt in
     ("-I")   fm="I"  ; shift 1 ;;  # integers
     ("-F")   fm="F"  ; shift 1 ;;  # floating point
     ("-E")   fm="E"  ; shift 1 ;;  # exponential notation
     ("--")  shift 1 ; break ;;
     # ----------------------------
     ("-"*)  status=64 ; shift 1 ;;
     # ----------------------------
     (*) break ;;
     # ----------------------------
    esac  # case ends here
  done  # while ends here
  

For the case of a user call, I already detect -- as an indicator for end of options. But when function gets called from another function, I would not have the capability to know beforehand whether the value is positive or negative.

How can I detect whether the argument contains characters that would invalidate the argument from being a number? Then I can introduce the check inside the "-"* part.



Solution 1:[1]

You could test for the pattern ("-"[0-9]*), which would then assume (at this point) that even -5X is a number. Well, it isn't a valid option either, and if you do a validity check on the numeric argument later on, you will catch this case anyway.

If we focus only on integers, another approach would be to explicitly test for the error case: The pattern -*[!0-9]* matches an argument which starts with a hyphen and contains something non-numeric afterwards and if this pattern matches, you know that you have an illegal option and not a number.

In your case, the first approach seems to be more sensible, because you seem to allow fractional numbers as well. Therefore, what is a valid number depends on what options has been set before (-I or -F), and you have to do the validation anyway based on the type of number you expect. For validating exponential formats, I would do a regex matching, not a glob pattern - depending on the kind of exponential format you allow:

if [[ $opt =~ ^[-+]?[0-9]+([.][0-9]+)?E[-+]?[0-9]+$ ]]
then
  # We have a valid number in exponential notation
  ...

Of course, the exact pattern to be used depends on what variations you allow in exponential notation.

Aside from this, your program is still problematic, because you allow that i.e. both -I and -E is passed to your command. In your current implementation, the last one is used and the preceding one are silently ignored. I would instead reconsider the design of your program, in that only one option (-I, -E or -F) may be supplied. This must be the first option, and everything after the second argument must then be a number.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 user1934428