How to stop getopts from taking blank or null or other options as option value

4.7k views Asked by At

I (presumably everyone) hit by this problem time to time but couldn't find any good workaround by myself. When getopts looks for an argument, it literally takes the next one, even if it's an option. This is what I did to stop that (code snippet):

#!/bin/bash 

function optsGet()
{
    while getopts ":c:f" opt; do
        case $opt in
            c ) [[ -z "${OPTARG}" || "${OPTARG}" == -* ]] \
                && { echo -e "ERROR: Invalid argument\n"; exit 1; }
                CNAME="${OPTARG}.tEsTsTr"
                ;;
            f ) FORCE=true
                ;;
            \?) echo -e "Invalid option: -$OPTARG\n" >&2;;
            : ) echo -e "Missing argument for -$OPTARG\n" >&2; exit 1;;
            * ) echo -e "Unimplemented option: -$OPTARG\n" >&2; exit 1;;
        esac
    done

    shift $(($OPTIND - 1))
}

optsGet "${@}"
echo -e "CNAME: ${CNAME}\n"

but it is still taking blank/null as a valid argument. So, this works:

san@AM0150 testtools$ ./getopts.sh -c -f
ERROR: Invalid argument

But these aren't:

san@AM0150 testtools$ ./getopts.sh -c " " -f
CNAME:  .tEsTsTr

san@AM0150 testtools$ ./getopts.sh -c \  -f
CNAME:  .tEsTsTr

I was rather expecting Missing argument for -c error. Is there anything I'm missing here? Or does anyone know workaround? Any input will be highly appreciate. Cheers!!


Update (mainly based on devnull's reply):

Just for the completeness, now I have this little function:

function ifEmpty()
{
    local VAL=$1
    local OPT=$2

    [[ -z "${VAL}" || "${VAL}" =~ ^[[:space:]]*$ || "${VAL}" == -* ]] \
    && { echo -e "\n  ERROR: Missing argument for option: -${OPT}\n" >&2; exit 1; }
}

then this can be used like this:

c ) ifEmpty "${OPTARG}" "${opt}"
    CNAME=${OPTARG//[[:space:]]}
    ;;

for all the options that needs an argument. Cheers!!

PS. for some reason, *[[:space:]]* doesn't work when used in the function.

2

There are 2 answers

4
devnull On BEST ANSWER

-z would return true for an empty string, not for a string containing spaces.

Check if the string consists only of whitespaces. Say:

    c ) [[ -z "${OPTARG}" || "${OPTARG}" =~ ^[[:space:]]*$ || "${OPTARG}" == -* ]] \

instead of

    c ) [[ -z "${OPTARG}" || "${OPTARG}" == -* ]] \

This would also handle the null (\) case.

EDIT: In fact, it can also be written as:

    c ) [[ -z "${OPTARG}" || "${OPTARG}" == *[[:space:]]* || "${OPTARG}" == -* ]] \
0
John Kugelman On

This may be a non-answer answer, but I would not try to stop it. If -c takes a file name as an argument, say, -f could be a valid file name, as could   (a space). As a general rule I don't think shell scripts should do "helpful" things like trimming whitespace or throwing out unusual-but-technically-valid arguments.