Home > database >  Optional arguments in Bash script
Optional arguments in Bash script

Time:03-05

I am trying to write a function that has optional arguments in a way such that the optional arguments are follow by something like -x, for example

my_function man_arg1 man_arg2 -n opt_arg1 -o opt_arg2 -x opt_arg3

and I want this to also support callings like

my_function man_arg1 man_arg2 -x opt_arg

In other questions I see people suggesting using getopts but in those answers it seems like you have to specify all the optional arguments when calling the function? Also it still seems unclear to me how you would use getopts for this.

CodePudding user response:

I am not sure I understood the question correctly, sorry if I don't answer it...

You could use (for example) getopt(1), as below, which will allow -x option to be anywhere. Please note that optional arguments (man_arg*) can also be anywhere.

#! /usr/bin/bash

CMD="${0##*/}"

my_function() {
    SOPTS="n:o:x:"
    TMP=$(getopt -o "$SOPTS" -n "$CMD" -- "$@") || exit 1
    eval set -- "$TMP"
    unset TMP

    while true; do
        case "$1" in
            -n)
                printf "[-n] argument: %s\n" "$2"
                shift
                ;;
            -o)
                printf "[-o] argument: %s\n" "$2"
                shift
                ;;
            -x)
                printf "[-x] argument: %s\n" "$2"
                shift
                ;;
            --)                                       # end of options
                shift
                break
                ;;

        esac
        shift
    done

    nargs=$#
    printf "remaining %d args :\n" "$nargs"
    for ((i=0; i<nargs;   i)); do
        printf "%d: %s\n" $((i   1)) "$1"
        shift
    done
}

my_function "$@"

Examples:

br@lorien:~$ ./test-getopt.bash man_arg1 man_arg2 -n opt_arg1 -o opt_arg2 -x opt_arg3
[-n] argument: opt_arg1
[-o] argument: opt_arg2
[-x] argument: opt_arg3
remaining 2 args :
1: man_arg1
2: man_arg2

br@lorien:~$ ./test-getopt.bash man_arg1 man_arg2 -n opt_arg1 -o opt_arg2 -x opt_arg3 man_arg3 man_arg4
[-n] argument: opt_arg1
[-o] argument: opt_arg2
[-x] argument: opt_arg3
remaining 4 args :
1: man_arg1
2: man_arg2
3: man_arg3
4: man_arg4

br@lorien:~$ ./test-getopt.bash man_arg1 man_arg2 -x opt_arg
[-x] argument: opt_arg
remaining 2 args :
1: man_arg1
2: man_arg2

br@lorien:~$ ./test-getopt.bash -x opt_arg4 man_arg2 -n opt_arg1 -x opt_arg3 man_arg3 man_arg4
[-x] argument: opt_arg4
[-n] argument: opt_arg1
[-x] argument: opt_arg3
remaining 3 args :
1: man_arg2
2: man_arg3
3: man_arg4

EDIT: Rewrote the code into a function, as asked in question.

CodePudding user response:

POSIX convention is that non-option arguments to a command must come after all options and option arguments. The POSIX getopts utility is designed around this convention, so if you don't want to insist on conventional argument order then getopts is not a good fit. It might still be possible to make getopts work in that case, but I wouldn't try to do so.

If you are willing to depend on the GNU version of the getopt utility (not to be confused with getopts, with an s), then it can accommodate you. By default, it recognizes options anywhere on the command line, including after non-option arguments. That might look like this:

args=$(getopt --name "$0" --options n:o:x: -- "$@")
eval set -- "$args"

while [[ $# -gt 0 ]]; do
  case "$1" in
    -n) n_argument=$2; shift 2;;
    -o) o_argument=$2; shift 2;;
    -x) x_argument=$2; shift 2;;
    *)
      # Handle non-option argument $1 ...
      shift
    ;;
  esac
done

But note that you can do much the same without getopt. What getopt (or getopts) does for you is:

  • normalize ganged options (-abc equivalent to -a -b -c; only for options that do not require arguments)
  • normalize option / argument separation (-xfoo equivalent to -x foo when option x takes an argument)
  • recognize and accommodate the significance of argument -- for indicating that only non-option arguments follow
  • recognize errors in the arguments, especially the omission of option arguments where they are required
  • similar to the above for GNU-style long options (GNU getopt only)

If you're not interested in any of that, or if you're willing to roll your own for the ones of those that you want, or if you can't rely on having the GNU version of getopt then you can use a loop similar to the above without first using getopt to massage the argument list, and without involving getopts.

  • Related