1

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

sicklybeans
  • 299
  • 3
  • 11
  • Command-line arguments are typically short strings. Consider reading from standard input to get `` instead. – chepner Oct 02 '13 at 21:11
  • How is the usage incorrect in the example you quote? What exactly is correct input? – rici Oct 02 '13 at 23:21
  • possible duplicate of [Using getopts in bash shell script to get long and short command line options](http://stackoverflow.com/questions/402377/using-getopts-in-bash-shell-script-to-get-long-and-short-command-line-options) – Jonathan Leffler Oct 03 '13 at 04:17
  • [How do I parse command line arguments in Bash?](https://stackoverflow.com/q/192249/608639), [How to iterate over arguments in a Bash script](https://stackoverflow.com/q/255898/608639), [Best way to parse command line args in Bash?](https://stackoverflow.com/q/14786984/608639), [What is the best way to parse command line options in bash shell?](https://stackoverflow.com/q/33060602/608639), [Parsing shell script arguments](https://stackoverflow.com/q/4882349/608639), etc. – jww Apr 12 '18 at 20:01

2 Answers2

3

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="$*"
rici
  • 234,347
  • 28
  • 237
  • 341
0

This is what I typically do:

local badflag=""
local aflag=""
local bflag=""
local cflag=""
local dflag=""

while [[ "$1" == -* ]]; do
  case $1 in
    -a)
      aflag="-a"
      ;;

    -b)
      bflag="-b"
      ;;

    -c)
      cflag="-c"
      ;;

    -d)
      dflag="-d"
      ;;

    *)
      badflag=$1
      ;;
  esac
  shift
done

if [ "$badflag" != "" ]; do
    echo "ERROR CONDITION"
fi

if [ "$1" == "" ] && [ "$dflag" == "" ]; do
    echo "ERROR CONDITION"
fi

local remaining_text=$@
evan
  • 12,307
  • 7
  • 37
  • 51
  • 2
    The (dis)advantage of this is that you cannot group single-letter options together; you must use each option explicitly on its own, with any option arguments in the next argument to the script. Thus, you can't simulate `ls -ls`, or `sort -ooutput` whereas using `getopts`, you can. – Jonathan Leffler Oct 03 '13 at 04:16