0

I want getopts to recognize when a user passes "-?" and "-h" and send to my help function. I also don't want getopts to send the user to the help function if an invalid option is presented. I want a different message to display telling them it was an invalid option and send to stderr.

Here is my options section:

options(){

    # grab options flags before entering application:
    while getopts ":h?vu:d" opts; do
        case "${opts}"
        in

            h\?)  # Help
                help_section
                exit 0
                ;;

            v)  # Version
                version
                exit 0
                ;;

            u)  # units
                other_units="$OPTARG"
                ;;

            d)  # Debug
                debug=1
                ;;

            \?) # Invalid options
                echo "no here"
                >&2 echo "[!] ERROR: Invalid Option: -$OPTARG"
                exit 1
                ;;


        esac
    done
    shift $((OPTIND -1))

    # Run main function
    main_run "$@"
}

Problem: getopts keeps sending the user to help when they put an invalid option in. I need to make sure the user recognizes they provided an invalid parameter; I do not want to send them to help.

Is there a way to implement this with getopts? Or build logic outside of getopts to capture and perform what I need?

Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
Dave
  • 727
  • 1
  • 9
  • 20
  • 2
    `h\?)` should be `h|\?)` to treat them as alternatives, not a single string. – Barmar Apr 01 '22 at 21:12
  • 4
    You can't use `-?` reliably at all: If a filename exists of the form `-h` or `-a` or whatnot in the directory where you try to run `yourscript -?`, the shell will treat `-?` as a glob expression and replace it with a list of matching names. Thus, this convention from the Windows world needs to **stay** in the Windows world; it breaks things when used on UNIXy platforms (unless you train your users to run `yourprogram -\?` or `yourprogram '-?'`, but if your user had already read the documentation with appropriate warnings they probably wouldn't be looking for help). – Charles Duffy Apr 01 '22 at 21:15
  • 2
    If you still want to do this, I think you have to check `$OPTARG` to distinguish a literal `-?` from an invalid option. See the documentation for how `getopts` sets `OPTARG` when an invalid option is found (it depends on whether it's in silent mode). – Barmar Apr 01 '22 at 21:18
  • @barmar, thanks for the idea. Can you elaborate or give an example? does every option need to use `$OPTARG` then? – Dave Apr 01 '22 at 21:19
  • No, you only need to check it in the `?` case, since that's the ambiguous one. – Barmar Apr 01 '22 at 21:19
  • BTW, [BashFAQ #35](https://mywiki.wooledge.org/BashFAQ/035) offers a command-line-parsing option that doesn't require `getopts` and thus bypass this issue altogether. – Charles Duffy Apr 01 '22 at 21:21

1 Answers1

1

Bash Command Line Input Example

The following example shows how to take various types of inputs and default to an invalid input option.

# -- Get input options (if any)
function get_user_input_options() {

    if [[ $# -lt 1 ]];then
       echo "no input, help infomration"
       #show_help_info_function
       exit
     fi

    while [[ $# > 0 ]] ;do
        key="$1"
        case ${key,,} in
            -o|--opt)
                echo " we a seting option=$2"
                option=2
                shift

            ;;
            -\?|-h|--\?|--help)
                echo  "help information"
                #show_help_info_function # a function that prints help and exits
                exit;
            ;;
            *)
                echo "not understanding your input at all!"
                exit;
            ;;
        esac
        shift
    done
}

get_user_input_options "$@"

What is going on here?

We read in all of the inputs and based on the type we can shift to the next or shift twice. If you see --opt is used, it is expecting something after which would be set to a variable. More on point of your issue, we accept -? or --? and if that happens it will do what is within the section of help; if none of these inputs is processed, it will do whatever the default is in the section '*' which means any input. Note: this is just an example and in this case the help section causes the script to stop even if other inputs were provided, this may or may not be what you personally are looking to do.

Example output

$ ./args.sh -o something
 we a seting option=something
$ ./args.sh -j somethingelse
not understading your input at all!
$ ./args.sh -?
help information
$ ./args.sh
no input, help infomration
$ ./args.sh -h
help information
Mike Q
  • 6,716
  • 5
  • 55
  • 62
  • 2
    In your case statement, you need to use `-\?|-h|--\?|--help` or `-[?]|-h|--[?]|--help` -- if you pass an invalid option (say `-x`) without the question mark escaped (it's a valid pattern metachar) then the help information is printed, not the error message. This is not a reliable method of option parsing. – glenn jackman Apr 01 '22 at 22:37
  • 1
    This solution works with `--` options but not with `-` options. For instance if I try to supply `-j` I still get the help information. – Dave Apr 04 '22 at 18:36
  • @Dave I think the escaping was the issue, I'm having success with the input on my ubuntu system. – Mike Q Apr 05 '22 at 18:15
  • @glennjackman right I should have done that. it was causing problems I think – Mike Q Apr 05 '22 at 18:16
  • @Dave I updated the code a little bit. Fixed the issue with escaping the question mark, added if no options were passed to the script and example of how to call a function that would perform the print help. – Mike Q Apr 05 '22 at 18:30
  • @Dave , let me know if you have any further questions. Thanks! Vote mine if you feel it answers your question which I believe it does. – Mike Q Apr 06 '22 at 15:57