Best way to parse command line args in Bash? Best way to parse command line args in Bash? bash bash

Best way to parse command line args in Bash?


I find the use of getopt to be the easiest. It provides correct handling of arguments which is tricky otherwise. For example, getopt will know how to handle arguments to a long option specified on the command line as --arg=option or --arg option.

What is useful in parsing any input passed to a shell script is the use of the "$@" variables. See the bash man page for how this differs from $@. It ensures that you can process arguments that include spaces.

Here's an example of how I might write s script to parse some simple command line arguments:

#!/bin/bashargs=$(getopt -l "searchpath:" -o "s:h" -- "$@")eval set -- "$args"while [ $# -ge 1 ]; do        case "$1" in                --)                    # No more options left.                    shift                    break                   ;;                -s|--searchpath)                        searchpath="$2"                        shift                        ;;                -h)                        echo "Display some help"                        exit 0                        ;;        esac        shiftdoneecho "searchpath: $searchpath"echo "remaining args: $*"

And used like this to show that spaces and quotes are preserved:

user@machine:~/bin$ ./getopt_test --searchpath "File with spaces and \"quotes\"."searchpath: File with spaces and "quotes".remaining args: other args

Some basic information about the use of getopt can be found here


If you want to avoid using getopt you can use this nice quick approach:

  • Defining help with all options as ## comments (customise as you wish).
  • Define for each option a function with same name.
  • Copy the last five lines of this script to your script (the magic).

Example script: log.sh

#!/bin/sh## $PROG 1.0 - Print logs [2017-10-01]## Compatible with bash and dash/POSIX## ## Usage: $PROG [OPTION...] [COMMAND]...## Options:##   -i, --log-info         Set log level to info (default)##   -q, --log-quiet        Set log level to quiet##   -l, --log MESSAGE      Log a message## Commands:##   -h, --help             Displays this help and exists##   -v, --version          Displays output version and exists## Examples:##   $PROG -i myscrip-simple.sh > myscript-full.sh##   $PROG -r myscrip-full.sh   > myscript-simple.shPROG=${0##*/}LOG=infodie() { echo $@ >&2; exit 2; }log_info() {  LOG=info}log_quiet() {  LOG=quiet}log() {  [ $LOG = info ] && echo "$1"; return 1 ## number of args used}help() {  grep "^##" "$0" | sed -e "s/^...//" -e "s/\$PROG/$PROG/g"; exit 0}version() {  help | head -1}[ $# = 0 ] && helpwhile [ $# -gt 0 ]; do  CMD=$(grep -m 1 -Po "^## *$1, --\K[^= ]*|^##.* --\K${1#--}(?:[= ])" log.sh | sed -e "s/-/_/g")  if [ -z "$CMD" ]; then echo "ERROR: Command '$1' not supported"; exit 1; fi  shift; eval "$CMD" $@ || shift $? 2> /dev/nulldone

Testing

Running this command:

./log.sh --log yep --log-quiet -l nop -i -l yes

Produces this output:

yepyes

By the way: It's compatible with !