6

I want to use both getopts and positional parameters, but if I pass in a positional parameter to the program the getopts get lost.

directory=$1

while getopts l: flag; do
  case "$flag" in
    l) level=$OPTARG;;
  esac
done

if [ -n "$level" ]; then
  echo "Level exist!"
else
  echo "Level doesn't exist!"
fi

So when I run the program like this:

sh myprogram.sh ~/documents -l 2

I expect:

Level exist!

And instead it returns:

Level doesn't exist!

The thing is, if I run the program without the positional parameter (~/documents) like this:

sh myprogram.sh -l 2

I get the correct output:

Level exist!

Why is that? How can I use both positional parameters and getopts in bash?

Thanks!

Cyrus
  • 84,225
  • 14
  • 89
  • 153
bntzio
  • 1,274
  • 14
  • 27
  • If you know for sure that the first positional parameter is going to be your directory, you can just use `shift` after the assignment `directory=$1`. But, as pointed out, it's probably better to just follow established practice. – Benjamin W. Sep 21 '17 at 20:41

1 Answers1

13

Most tools are written in the form: tool [options] arg ...

So you would do this:

# first, parse the options:
while getopts l: flag; do
  case "$flag" in
    l) level=$OPTARG;;
    \?) exit 42;;
  esac
done

# and shift them away
shift $((OPTIND - 1))

# validation
if [ -n "$level" ]; then
  echo "Level exist!"
else
  echo "Level doesn't exist!"
fi

# THEN, access the positional params
echo "there are $# positional params remaining"
for ((i=1; i<=$#; i++)); do
  printf "%d\t%s\n" $i "${!i}"
done

Use the \? to abort the script if the user provides an unknown option or fails to provide a required argument

And invoke it like:

$ bash test.sh
Level doesn't exist!
there are 0 positional params remaining

$ bash test.sh -l 2
Level exist!
there are 0 positional params remaining

$ bash test.sh -l 2 foo bar
Level exist!
there are 2 positional params remaining
1   foo
2   bar

$ bash test.sh -x
test.sh: illegal option -- x

$ bash test.sh -l
test.sh: option requires an argument -- l

But you cannot put the options after the arguments: getopts stops when the first non-option argument is found

$ bash test.sh foo bar -l 2
Level doesn't exist!
there are 4 positional params remaining
1   foo
2   bar
3   -l
4   2
glenn jackman
  • 238,783
  • 38
  • 220
  • 352
  • Nice! It worked, so there isn't any workaround to put the opts after the params? – bntzio Sep 21 '17 at 20:13
  • Not with `getopts`. There is an external program call `getopt` that can do it. It's a little harder to work with. Here's an example: https://bazaar.launchpad.net/~ubuntu-branches/ubuntu/vivid/util-linux/vivid/view/head:/misc-utils/getopt-parse.bash – glenn jackman Sep 21 '17 at 20:30