There are a number of questions about this sort of thing but lets imagine we are targeting a generic Linux system with both getopt and getopts installed (not that we'll use either, but they seem popular)
How do I parse both long (--example | --example simple-option) and short argruments (-e | -esimple-example | -e simple-example)
Bash scripts take in command-line arguments as inputs both sequentially and also, parsed as options. The command-line utilities use these arguments to conditionally trigger functions in a Bash script or selectively choose between environments to execute the script. In Bash, these are configured in different ways.
It mainly utilize the “internal field separator” or IFS , and the read builtin function. The first step is to split the string using new line, with IFS=$'\n' . Then we split using the colon : , and use read -ra vals to split the input and put it in an array.
The special character $# stores the total number of arguments. We also have $@ and $* as wildcard characters which are used to denote all the arguments. We use $$ to find the process ID of the current shell script, while $? can be used to print the exit code for our script.
To parse short command-line options, we can use bash's built-in function getopts. It parses positional parameters as options.
You want to use getopt
with long and short options. An example from working code:
# Parse arguments
TEMP=$(getopt -n $PROGRAM_NAME -o p:P:cCkhnvVS \
--long domain-password:,pop3-password:\
,create,cron,kill,help,no-sync-passwords,version,verbose,skip-pop3 \
-- "$@")
# Die if they fat finger arguments, this program will be run as root
[ $? = 0 ] || die "Error parsing arguments. Try $PROGRAM_NAME --help"
eval set -- "$TEMP"
while true; do
case $1 in
-c|--create)
MODE="CREATE"; shift; continue
;;
-C|--cron)
MODE="CRON"; shift; continue
;;
-k|--kill)
MODE="KILL"; shift; continue
;;
-h|--help)
usage
exit 0
;;
-n|--no-sync-passwords)
SYNC_VHOST=0; shift; continue
;;
-p|--domain-password)
DOMAIN_PASS="$2"; shift; shift; continue
;;
-P|--pop3-password)
POP3_PASS="$2"; shift; shift; continue
;;
-v|--version)
printf "%s, version %s\n" "$PROGRAM_NAME" "$PROGRAM_VERSION"
exit 0
;;
-v|--verbose)
VERBOSE=1; shift; continue
;;
-S|--skip-pop3)
SKIP_POP=1; shift; continue
;;
--)
# no more arguments to parse
break
;;
*)
printf "Unknown option %s\n" "$1"
exit 1
;;
esac
done
Note, die
is a function that was defined previously (not shown).
The -n
option tells getopt to report errors as the name of my program, not as getopt
. -o
defines a list of short options (:
after an option indicates a needed argument) and --long
specifies the list of long options (corresponding in order to the short options).
The rest is just a simple switch, calling shift
appropriately to advance the argument pointer. Note, calling shift; shift;
is just a die hard habit. In the currently modern world, shift 2
would probably suffice.
The modern getopt is pretty consistent over newer platforms, however you may encounter some portability problems on older (circa pre Redhat 9) systems. See man getopt
for information about backwards compatibility. However it's unlikely that you'll run into the need for it.
Finally, after parsing options, you can once again call:
eval set -- "$@"
This will move the argument pointer to anything else left on the command line after getopt was done parsing options. You can then just shift
to keep reading them. For instance, if a command looked like this:
./foo --option bar file1.txt file2.txt file3.txt
Don't forget to make a handy -h / --help
option to print your new fancy options once you're done. :) If you make that output help2man friendly, you have an instant man page to go with your new tool.
Edit
On most distributions, you can find more example getopt
code in /usr/share/doc/util-linux/examples
, which should have been installed by default.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With