1

In a script which request some arguments (arg) and options (-a), I would like to let the script user the possibility to place the options where he wants in the command line.

Here is my code :

while getopts "a" opt; do
  echo "$opt"
done
shift $((OPTIND-1))

echo "all_end : $*"

With this order, I have the expected behaviour :

./test.sh -a arg
a
all_end : arg

I would like to get the same result with this order :

./test.sh arg -a
all_end : arg -a
9
  • Most of the linux commands support this feature. I don't want my users to be lost. Example : tail file -n 5 and tail -n 5 file works. Commented Jan 25, 2021 at 14:02
  • That's different from "tail 5 -n", that is what you want and it's impossible Commented Jan 25, 2021 at 14:11
  • 2
    I think you'd have to first modify the positional parameters in $@ and remove all non-option arguments. getopts on its own won't be able to support what you're asking for. Commented Jan 25, 2021 at 14:11
  • 1
    Most linux commands are not written in Bourne shell, and use something like the GNU getops_long library call for options processing (or something entirely different; most languages have their own standard -- or multiple -- options processing modules). You could of course write your own options processing routine in shell script that does what you want. Commented Jan 25, 2021 at 14:20
  • 1
    A rearranging solution is described in Complex option parsing. Commented Jan 25, 2021 at 14:30

4 Answers 4

5

The getopt command (part of the util-linux package and different from getopts) will do what you want. The bash faq has some opinions about using that, but honestly these days most systems will have the modern version of getopt.

Consider the following example:

#!/bin/sh

options=$(getopt -o o: --long option: -- "$@")
eval set -- "$options"

while :; do
    case "$1" in
        -o|--option)
            shift
            OPTION=$1
            ;;
        --)
            shift
            break
            ;;
    esac

    shift
done

echo "got option: $OPTION"
echo "remaining args are: $@"

We can call this like this:

$ ./options.sh -o foo arg1 arg2
got option: foo
remaining args are: arg1 arg2

Or like this:

$ ./options.sh  arg1 arg2 -o foo
got option: foo
remaining args are: arg1 arg2
Sign up to request clarification or add additional context in comments.

Comments

2

In the many years of bash programming, I've found it useful to get rid of the bone in my head that says functions should look like f(x,y), especially since bash requires clumsy/inefficient code to handle command line arguments.

Option arguments often have default values, and can be more readily passed as environmental variables whose scope is limited to the called script, and save arguments for what must be provided.

Applying this to your example, The script would look like:

OPTION=${OPTION:="fee"}
echo "option: $OPTION"
echo "remaining args are: $*"

And would be called with:

OPTION="foo" ./options.sh arg1 arg2

Comments

1

You can still go with argument parsing by looking at each of them

#!/bin/bash

for i in "$@"
do
case $i in
    -a)
    ARG1="set"
    shift
    ;;
    *)
    # the rest (not -a)
    ARGS="${ARGS} $i"
    ;;
esac
done

if [ -z "$ARG1" ]; then
  echo "You haven't passed -a"
else
  echo "You have passed -a"
fi

echo "You have also passed: ${ARGS}"

and then you will get:

> ./script.sh arg -a
You have passed -a
You have also passed:  arg
> ./script.sh -a arg
You have passed -a
You have also passed:  arg
> ./script.sh -a
You have passed -a
You have also passed:
> ./script.sh arg
You haven't passed -a
You have also passed:  arg

Comments

1

Consider this approach

#!/bin/bash
    
opt(){
    case $1 in
        -o|--option) option="$2";;
        -h|--help  ) echo "$help"; exit 0;;
                  *) echo "unknown option: $1"; exit 1;;
    esac
}

while [[ $@ ]]; do
    case $1 in
      arg1) var1=$1 ;;
      arg2) var2=$1 ;;
        -*) opt "$@"; shift;;
         *) echo "unknown option: $1"; exit 1;;
    esac
    shift
done

echo args: $var1 $var2
echo opts: $option

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.