11

I am trying to process command line arguments using getopts in bash. One of the requirements is for the processing of an arbitrary number of option arguments (without the use of quotes).

1st example (only grabs the 1st argument)

madcap:~/projects$ ./getoptz.sh -s a b c
-s was triggered
Argument: a

2nd example (I want it to behave like this but without needing to quote the argument"

madcap:~/projects$ ./getoptz.sh -s "a b c"
-s was triggered
Argument: a b c

Is there a way to do this?

Here's the code I have now:

#!/bin/bash
while getopts ":s:" opt; do
    case $opt in
    s) echo "-s was triggered" >&2
       args="$OPTARG"
       echo "Argument: $args"
       ;;
   \?) echo "Invalid option: -$OPTARG" >&2
       ;;
    :) echo "Option -$OPTARG requires an argument." >&2
       exit 1
       ;;
    esac
done
3

4 Answers 4

17

I think what you want is to get a list of values from a single option. For that, you can repeat the option as many times as needed, and add it's argument to an array.

#!/bin/bash

while getopts "m:" opt; do
    case $opt in
        m) multi+=("$OPTARG");;
        #...
    esac
done
shift $((OPTIND -1))

echo "The first value of the array 'multi' is '$multi'"
echo "The whole list of values is '${multi[@]}'"

echo "Or:"

for val in "${multi[@]}"; do
    echo " - $val"
done

The output would be:

$ /tmp/t
The first value of the array 'multi' is ''
The whole list of values is ''
Or:

$ /tmp/t -m "one arg with spaces"
The first value of the array 'multi' is 'one arg with spaces'
The whole list of values is 'one arg with spaces'
Or:
 - one arg with spaces

$ /tmp/t -m one -m "second argument" -m three
The first value of the array 'multi' is 'one'
The whole list of values is 'one second argument three'
Or:
 - one
 - second argument
 - three
Sign up to request clarification or add additional context in comments.

Comments

7

This's my way to do this with a user-defined-function: getopts-extra.

function getopts-extra () {
    declare i=1
    # if the next argument is not an option, then append it to array OPTARG
    while [[ ${OPTIND} -le $# && ${!OPTIND:0:1} != '-' ]]; do
        OPTARG[i]=${!OPTIND}
        let i++ OPTIND++
    done
}

# Use it within the context of `getopts`:
while getopts s: opt; do
    case $opt in
       s) getopts-extra "$@"
          args=( "${OPTARG[@]}" )
    esac
done

The full example of getoptz.sh:

#!/usr/bin/env bash

function getopts-extra () {
    declare i=1
    # if the next argument is not an option, then append it to array OPTARG
    while [[ ${OPTIND} -le $# && ${!OPTIND:0:1} != '-' ]]; do
        OPTARG[i]=${!OPTIND}
        let i++ OPTIND++
    done
}

function main () {
    declare args
    declare OPTIND OPTARG opt
    while getopts :s: opt; do
        case $opt in
        s) getopts-extra "$@"
           args=( "${OPTARG[@]}" )
           ;;
       \?) echo "Invalid option: -$OPTARG" >&2
           ;;
        :) echo "Option -$OPTARG requires an argument." >&2
           exit 1
           ;;
        esac
   done

   declare i
   for i in "${!args[@]}"; do
       echo "args[$i]: ${args[i]}"
   done
}

main "$@"

exit

Test:

bash getoptz.sh -s a b c

Output:

args[0]: a
args[1]: b
args[2]: c

This function is a part of the bash lib called xsh-lib/core, available at the syntax xsh /util/getopts/extra.

3 Comments

Great Idea but I just got some questions on this scripts: with getopts-extra func we could only transmit arguments start with the second one, and looks like we use args=( "${OPTARG[@]}" ) to transmit the first arguments. And I'm just wondering why it cannot pass all arguments in func, and is it possible to pass it all by one function only?
@Strategy, sorry I failed to understand your question. could you explain more about the use case or give an example input and the expected output.
ok i.e. input: bash getoptz.sh -s a b c, code: #!/bin/bash args="" function getopts-extra () { while [[ ${OPTIND} -le $# && ${!OPTIND:0:1} != '-' ]]; do args="${args} add ${!OPTIND}" let OPTIND++ done } function main () { declare OPTIND OPTARG opt while getopts :s: opt; do case $opt in s) g "$@" ;; esac done echo $args } main "$@" exit expect output: add a add b add c actual output: add b add c ("add a" missing)
3

You can parse the command-line arguments yourself, but the getopts command cannot be configured to recognize multiple arguments to a single option. fedorqui's recommendation is a good alternative.

Here is one way of parsing the option yourself:

while [[ "$*" ]]; do
    if [[ $1 = "-s" ]]; then
        # -s takes three arguments
        args="$2 $3 $4"
        echo "-s got $args"
        shift 4
    fi
done

1 Comment

Indeed, "recognize multiple arguments to a single option" is not possible, but the option can be repeated. I added an example to the answers, because I think that is what is really wanted.
0

Instead of using getops I found this better. Create a file tmp.sh and copy the following code..

usage() {
    echo "Usage: 
    ./tmp.sh --cloud_provider gcp --source_path abc --destination_path def --refresh_frequency 100 --authenticate true --credential_path a.json
    "
}


while [ "$#" -gt 0 ]; do
    case $1 in
    -a | --authenticate)
        shift
        [[ $1 == -* ]] && continue
        export authenticate=$1
        ;;
    -cp | --credential_path)
        shift
        [[ $1 == -* ]] && continue
        export credential_path=$1
        ;;
    -c | --cloud_provider)
        shift
        [[ $1 == -* ]] && continue
        export cloud_provider=$1
        ;;
    -s | --source_path)
        shift
        [[ $1 == -* ]] && continue
        export source_path=$1
        ;;
    -d | --destination_path)
        shift
        [[ $1 == -* ]] && continue
        export destination_path=$1
        ;;
    -r | --refresh_frequency)
        shift
        [[ $1 == -* ]] && continue
        export refresh_frequency=$1
        ;;
    --)
        shift
        break
        ;;
    esac
    shift
done

1 Comment

Your script doesn't answer the original question. OP wanted script than something like ./script.sh -s a b c

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.