Search code examples
bashshellcommand-line-argumentsoption-typegetopts

Optional arguments in Bash script


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.


Solution

  • 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 is the shell-script name without directory (basename)
    CMD="${0##*/}"
    
    my_function() {
        # Some comments added per OP request
    
        # below variable is used by getopt's "-o" option (short options).
        # each letter is a possible option; colon following a letter means this
        # option will take a parameter. See getopt(1) for more details.
        SOPTS="n:o:x:"
    
        # output of getopt is assigned to TMP. For example, when calling :
        # my_function -n n1 a b -o o1 -x x1 c d
        # TMP will contain: "-n 'n1' -o 'o1' -x 'x1' -- 'a' 'b' 'c' 'd'"
        TMP=$(getopt -o "$SOPTS" -n "$CMD" -- "$@") || exit 1
    
        # assign TMP to positional parameters $1, $2, etc...
        eval set -- "$TMP"
        unset TMP
    
        while true; do
            case "$1" in
                -n|-o)
                    printf "[%s] argument: %s\n" "$1" "$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.