Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to combine getopts and positional parameters in bash? [duplicate]

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!

like image 376
bntzio Avatar asked Sep 21 '17 19:09

bntzio


People also ask

Can I handle a mix of positional parameters and Getopt values?

Yes. I want to enable my script to handle a mix of positional parameters and getopt values. everything which is remaining from getopt should be passed with underlying shell script. Only the first line is compliant with the unix standards (see below) for option processing. Doing it otherwise will be a lot more work to get right, and to maintain.

What is the use of getopts in Bash shell?

With getopts, the Bash shell is running your script and the Bash shell is doing the option parsing. You don’t need to invoke an external program to handle the parsing. The tradeoff is getopts doesn’t handle double-dashed, long-format option names. So you can use options formatted like -w but not ” ---wide-format .”

How do I ADD arguments to getopts options?

The options expected by getopts are -a, -p, and -Z, with no arguments. These options can be combined in any order as -aZ, -pa, -Zap, etc. Let's say that you'd like the -a and -Z options to take arguments. You can specify this by putting a colon (": ") after that option in optstring.

How does getopts work?

If the option is expecting an argument, getopts gets that argument, and places it in $OPTARG. If an expected argument is not found, the variable optname is set to a colon (": "). Getopts then increments the positional index, $OPTIND, that indicates the next option to be processed.


Video Answer


1 Answers

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
like image 197
glenn jackman Avatar answered Oct 26 '22 04:10

glenn jackman