BASH getopt command returns its own parameters instead of command line parameters-Collection of common programming errors

I am creating a BASH script and working with the BASH getopt command to parse command line arguments. Instead of returning the arguments provided on the command line, getopt returns the arguments that were supplied to the getopt command. I am not sure what could be going on, as the code that I have was working perfect, and seemingly out of nowhere, it has stoped working correctly (and no, I haven’t updated anything or changed any code or environment settings). I can’t use getopts (with the extra ‘s’) because it is, for some unknown reason, not installed on the machine that will be running this script.

Even though the script is supplied with zero command line arguments, the getopt command is for some reason returning all of the arguments that I have supplied, minus the -o flag, instead of the expected -- value indicating the end of the options. The code that I have is as follows:

SHORT_OPTS=":hvso:l:d:n:p:t:"
LONG_OPTS="help,version,submit-job,output:,library:,job-dir:"
LONG_OPTS="${LONG_OPTS},num-nodes:,num-procs:,max-time:"
OPTS=$(getopt -o "${SHORT_OPTS}" -l "${LONG_OPTS}" -a -n "${PROG_NAME}" -- "${@}")

# Check for invalid command line options and arguments
if [[ ${?} -ne ${SUCCESS} ]] ; then
    echo -e "${PROG_NAME}: error: Invalid option or argument\n" >&2
    usage ; exit ${FAILURE}
else
    echo "BEFORE $@"
    eval set -- ${OPTS}
    echo "AFTER $@"
fi

# Process command line options and their arguments
while true ; do
    case "${1}" in
        -h | --help) 
            # Display script usage information and exit
            usage ; exit ${SUCCESS} ;;
        -v | --version) 
            # Display script version information and exit
            echo "${PROG_NAME} v${PROG_VERSION}" ; exit ${SUCCESS} ;;
        -s | --submit-job) 
            # Enable automatic submission of the Moab job
            JOB_AUTO_SUBMIT="${PREF_YES}" ; shift 1 ;;
        -o | --output) 
            # Set the base name for output file names
            TARGET="${2}" ; shift 2 ;;
        -l | --library) 
            # Set the library to use for NWChem atomic configurations
            NW_LIB="${2}" ; shift 2 ;;
        -d | --job-dir) 
            # Ensure the specified directory for the Moab job exists
            if [[ -e "${2}" ]] ; then
                JOB_WORK_DIR=$(resolvePath "${2}") ; shift 2
            else
                echo -e "${PROG_NAME}: error: -d ${2}: No such directory\n"
                usage ; exit ${FAILURE}
            fi ;;
        -n | --num-nodes) 
            # Ensure the number of compute nodes is greater than zero
            if positiveInt "${2}" ; then
                JOB_NODES="${2}" ; shift 2
            else
                echo -n "${PROG_NAME}: error: -n ${1}: Number of "
                echo -e "job nodes must be a positive integer\n"
                usage ; exit ${FAILURE}
            fi ;;
        -p | --num-procs) 
            # Ensure the number of processors per node is greater than zero
            if positiveInt "${2}" ; then
                JOB_PROCS="${2}" ; shift 2
            else
                echo -n "${PROG_NAME}: error: -p ${2}: Number of "
                echo -e "processors per node must be a positive integer\n"
                usage ; exit ${FAILURE}
            fi ;;
        -t | --max-time) 
            # Ensure the maximum job runtime is in the correct format
            if [[ "${2}" == [0-9][0-9]:[0-9][0-9]:[0-9][0-9] ]] ; then
                JOB_MAX_TIME="${2}" ; shift 2
            else
                echo -n "${PROG_NAME}: error: -t ${2}: Invalid time "
                echo -e "format, please use hh:mm:ss format\n"
                usage ; exit ${FAILURE}
            fi ;;
        --) 
            # No more options to process
            shift ; break ;;
    esac
done

# Check to see if POTCAR and CONTCAR locations were specified
if [[ ${#} -eq 2 ]] ; then
    # Regular expressions for identifying POTCAR and CONTCAR files
    PCAR_REGEX="[Pp][Oo][Tt][Cc][Aa][Rr]"
    CCAR_REGEX="[Cc][Oo][Nn][Tt][Cc][Aa][Rr]"

    # Attempt to identify POTCAR and CONTCAR argument ordering
    if [[ ${1} =~ ${PCAR_REGEX} && ${2} =~ ${CCAR_REGEX} ]] ; then
        POTCAR="${1}" ; CONTCAR="${2}" ; shift 2
    else
        POTCAR="${2}" ; CONTCAR="${1}" ; shift 2
    fi
# Accept exactly two or zero command line arguments
elif [[ ${#} -ne 0 ]] ; then
    echo "${PROG_NAME}: error: ${#}: Invalid argument count, expected [2|0]"
    echo "$@"
    exit ${FAILURE}
fi

Given this code, and running the application, I get the following output:

BEFORE 
AFTER -- :hvso:l:d:n:p:t: -l help,version,submit-job,output:,library:,job-dir:,num-nodes:,num-procs:,max-time: -a -n vasp2nwchem --
vasp2nwchem: error: 7: Invalid argument count, expected [2|0]
:hvso:l:d:n:p:t: -l help,version,submit-job,output:,library:,job-dir:,num-nodes:,num-procs:,max-time: -a -n vasp2nwchem --

So, the code enters the while loop portion of the code, jumps to the last case, and shifts off the first --, leaving me with all of the arguments that I supplied to getopt, minus the -o flag.

Any light that anyone could shed on this conundrum would be immensely appreciated, because it is seriously about to send me over the edge, especially since this code was functional no less than thrity minutes ago, and has now stopped working entirely!!!

  1. I don’t see anything wrong. I have GNU getopt installed as /usr/gnu/bin/getopt (and BSD getopt in /usr/bin), so this script (chk.getopt.sh) is almost equivalent to the start of yours, though I do set the PROG_NAME variable. This is more or less the SSCCE (Short, Self-Contained, Complete Example) for your rather substantial script.

    #!/bin/bash
    
    PROG_NAME=$(basename $0 .sh)
    SHORT_OPTS=":hvso:l:d:n:p:t:"
    LONG_OPTS="help,version,submit-job,output:,library:,job-dir:"
    LONG_OPTS="${LONG_OPTS},num-nodes:,num-procs:,max-time:"
    OPTS=$(/usr/gnu/bin/getopt -o "${SHORT_OPTS}" -l "${LONG_OPTS}" -a -n "${PROG_NAME}" -- "$@")
    
    # Check for invalid command line options and arguments
    if [[ ${?} -ne ${SUCCESS} ]] ; then
        echo -e "${PROG_NAME}: error: Invalid option or argument\n" >&2
        usage ; exit ${FAILURE}
    else
        echo "BEFORE $@"
        eval set -- ${OPTS}
        echo "AFTER $@"
    fi
    

    When I run it, this is the output:

    $ bash -x chk.getopt.sh  -ooutput -nnumber -pperhaps -ppotato -- -o oliphaunt
    ++ basename chk.getopt.sh .sh
    + PROG_NAME=chk.getopt
    + SHORT_OPTS=:hvso:l:d:n:p:t:
    + LONG_OPTS=help,version,submit-job,output:,library:,job-dir:
    + LONG_OPTS=help,version,submit-job,output:,library:,job-dir:,num-nodes:,num-procs:,max-time:
    ++ /usr/gnu/bin/getopt -o :hvso:l:d:n:p:t: -l help,version,submit-job,output:,library:,job-dir:,num-nodes:,num-procs:,max-time: -a -n chk.getopt -- -ooutput -nnumber -pperhaps -ppotato -- -o oliphaunt
    + OPTS=' -o '\''output'\'' -n '\''number'\'' -p '\''perhaps'\'' -p '\''potato'\'' -- '\''-o'\'' '\''oliphaunt'\'''
    + [[ 0 -ne '' ]]
    + echo 'BEFORE -ooutput' -nnumber -pperhaps -ppotato -- -o oliphaunt
    BEFORE -ooutput -nnumber -pperhaps -ppotato -- -o oliphaunt
    + eval set -- -o ''\''output'\''' -n ''\''number'\''' -p ''\''perhaps'\''' -p ''\''potato'\''' -- ''\''-o'\''' ''\''oliphaunt'\'''
    ++ set -- -o output -n number -p perhaps -p potato -- -o oliphaunt
    + echo 'AFTER -o' output -n number -p perhaps -p potato -- -o oliphaunt
    AFTER -o output -n number -p perhaps -p potato -- -o oliphaunt
    $
    

    This all looks correct; the options before the double dash have been split from their arguments, and the ones after the double dash are OK.

    So, it is not obvious that there’s a problem with your code. Even with an empty string as the program name, it worked OK for me.

    May be you should show the output of this much of the script on your machine?

Originally posted 2013-11-09 21:07:50.