I have copied code from tutorialspoint's getopt article and got the following script to work (sort of):

VARS=`getopt -o i::o:: --long input::,output:: -- "$@"`
eval set -- "$VARS"

# extract options and their arguments into variables.
while true ; do
    case "$1" in

            case "$2" in
                "") MAPPE='/default/input/here/' ; shift 2 ;;
                *) MAPPE=$2 ; shift 2 ;;
            esac ;;
            case "$2" in
                "") OUTPUTFOLDER='/default/input/here/' ; shift 2 ;;
                *) OUTPUTFOLDER=$2 ; shift 2 ;;
        esac ;;
        --) shift ; break ;;

echo "${MAPPE}"

#do something here..

that is, I have two optional argument flags -i/--input and -o/-output. I have a problem with the script currently:

To overwrite the default value of a flag, you need to write the value you want right after the flag, without any spaces. example: if i wanted to pass /c/ into -i and /f/ into -o, i would need to call the script as: bash argument_script.sh -i/c/ -o/f/. Notice the missing spaces. If I were to write bash argument_script.sh -i /c/ -o /f/ the variables MAPPE and OUTPUTFOLDER would be using the default values.
Can the script be rewritten, so the arguments passed into -i/-o needs to be written after a space (example: bash argument_script.sh -i /c/ -o /f/)

  • Do you have a #! line as the first line of your script?
    – roaima
    Jan 13 '21 at 9:24
  • That can't work. If you allow the next argument after -i to be its value, then there's no other way to pass -i without value than by making sure -i is the last argument. As cmd -i -o would be -i with -o as the option's argument (like --input=-o) Jan 13 '21 at 10:52

The behavior you describe is because you have an extra : in your getopt. Just change your getopt line to this and it will work:

VARS=`getopt -o i:o: --long input:,output: -- "$@"`

However, this is a very, very convoluted way of writing your script. Here is a simpler version (also correcting some bad practices like capitalized variables):


vars=$(getopt -o i:o: --long input:,output: -- "$@")
eval set -- "$vars"


# extract options and their arguments into variables.
for opt; do
    case "$opt" in
        shift 2
        shift 2
echo "mappe: $mappe"
echo "out: $outputFolder"

You can now do:

$ ./argument_script.sh -i /c/  -o /f/
mappe: /c/
out: /f/

Note that it also works if you run ./argument_script.sh -i/c/ -o/f/. The space is not required, it is allowed.


That's the expected behaviour, unfortunately, as implemented by the GNU getopt(3) function the getopt(1) utility is based on. From its manpage (emphasis mine):

Two colons mean an option takes an optional arg; if there is text in the current argv-element (i.e., in the same word as the option name itself, for example, -oarg), then it is returned in optarg, otherwise optarg is set to zero.

If you want to be able to use a space between the option and its argument (e.g. -o value instead of -ovalue) you have to make its argument non-optional, by using a single : after the option char in the spec.

  • 1
    The option is still optional. If you use the version in my answer, both options are optional. You can't pass the option with no argument but that is normal: this isn't an option that makes sense without an argument.
    – terdon
    Jan 13 '21 at 11:02

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.