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!!!
-
I don’t see anything wrong. I have GNU
getopt
installed as/usr/gnu/bin/getopt
(and BSDgetopt
in/usr/bin
), so this script (chk.getopt.sh
) is almost equivalent to the start of yours, though I do set thePROG_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.