Best way to parse command line args in Bash?

bash, command-line, getopts

Solution

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/bash

args=$(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

        shift
done

echo "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

Problem

After several days of research, I still can't figure out the best method for parsing cmdline args in a .sh script. According to my references the getopts cmd is the way to go since it "extracts and checks switches without disturbing the positional parameter variables.Unexpected switches, or switches that are missing arguments, are recognized and reportedas errors." Positional params(Ex. 2 - $@, $#, etc) apparently don't work well when spaces are involved but can recognize regular and long parameters(-p and --longparam). I noticed that both methods fail when passing parameters with nested quotes ("this is an Ex. of ""quotes""."). Which one of these three code samples best illustrates the way to deal with cmdline args? The getopt function is not recommended by gurus, so I'm trying to avoid it! Example 1: ``` #!/bin/bash for i in "$@" do case $i in -p=*|--prefix=*) PREFIX=`echo $i | sed 's/[-a-zA-Z0-9]*=//'` ;; -s=*|--searchpath=*) SEARCHPATH=`echo $i | sed 's/[-a-zA-Z0-9]*=//'` ;; -l=*|--lib=*) DIR=`echo $i | sed 's/[-a-zA-Z0-9]*=//'` ;; --default) DEFAULT=YES ;; *) # unknown option ;; esac done exit 0 ``` Example 2: ``` #!/bin/bash echo ‘number of arguments’ echo "\$#: $#" echo ” echo ‘using $num’ echo "\$0: $0" if [ $# -ge 1 ];then echo "\$1: $1"; fi if [ $# -ge 2 ];then echo "\$2: $2"; fi if [ $# -ge 3 ];then echo "\$3: $3"; fi if [ $# -ge 4 ];then echo "\$4: $4"; fi if [ $# -ge 5 ];then echo "\$5: $5"; fi echo ” echo ‘using $@’ let i=1 for x in $@; do echo "$i: $x" let i=$i+1 done echo ” echo ‘using $*’ let i=1 for x in $*; do echo "$i: $x" let i=$i+1 done echo ” let i=1 echo ‘using shift’ while [ $# -gt 0 ] do echo "$i: $1" let i=$i+1 shift done [/bash] output: bash> commandLineArguments.bash number of arguments $#: 0 using $num $0: ./commandLineArguments.bash using $@ using $* using shift #bash> commandLineArguments.bash "abc def" g h i j* ``` Example 3: ``` #!/bin/bash while getopts ":a:" opt; do case $opt in a) echo "-a was triggered, Parameter: $OPTARG" >&2 ;; \?) echo "Invalid option: -$OPTARG" >&2 exit 1 ;; :) echo "Option -$OPTARG requires an argument." >&2 exit 1 ;; esac done exit 0 ```

Original source

Related problems