Search code examples
bashcommand-line-argumentsgetoptgetopts

Best way to parse arguments in bash script


So I've been reading around about getopts, getopt, etc. but I haven't found an exact solution to my problem.

The basic idea of the usage of my script is:

./program [-u] [-s] [-d] <TEXT>

Except TEXT is not required if -d is passed. Note that TEXT is usually a paragraph of text.

My main problem is that once getopts finishing parsing the flags, I have no way of knowing the position of the TEXT parameter. I could just assume that TEXT is the last argument, however, if a user messes up and does something like:

./program -u "sentence 1" "sentence 2"

then the program will not realize that the usage is incorrect.

The closest I've come is using getopt and IFS by doing

ARGS=$(getopt usd: $*)
IFS=' ' read -a array <<< "$ARGS"

The only problem is that TEXT might be a long paragraph of text and this method splits every word of text because of the spaces.

I'm thinking my best bet is to use a regular expression to ensure the usage is correctly formed and then extract the arguments with getopts, but it would be nice if there was a simpler solution


Solution

  • It's quite simple with getopts:

    #!/bin/bash
    u_set=0
    s_set=0
    d_set=0
    while getopts usd OPT; do
      case "$OPT" in
        u) u_set=1;;
        s) s_set=1;;
        d) d_set=1;;
        *) # getopts produces error
           exit 1;;
      esac
    done
    if ((!d_set && OPTIND>$#)); then
      echo You must provide text or use -d >>/dev/stderr
      exit 1
    fi
    # The easiest way to get rid of the processed options:
    shift $((OPTIND-1))
    # This will run all of the remaining arguments together with spaces between them:
    TEXT="$*"