0

I am re-learning getopt with a tiny script in bash, but the second parameters fall into default branch of case.

#! /bin/bash

LONG_OPTION_LIST=( "arg-a" "arg-b:" "arg-c:" ) SORT_OPTION_LIST=( "a" "b:" "c:" )

Read the parameters

opts=$(getopt -q
--longoptions "$(printf "%s," "${LONG_OPTION_LIST[@]}")"
--name "$(basename "$0")"
--options "$(printf "%s" "${SORT_OPTION_LIST[@]}")"
-- "$@" ) eval set -- "$opts"

echo "##$1##" echo "##$2##" echo "##$3##" echo "##$4##" echo "##$5##" echo "#########"

argA=0

It it is same a queue (process the head) because $1 and $2

for arg do echo $1 echo $2 echo "--------" case "$arg" in --arg-a | -a) argA=1 shift 1 ;; --arg-b | -b) argB=$2 shift 2 ;; --arg-c | -c) argC=$2 shift 2 ;; *) echo "###$1###" echo "break" echo "_________" break ;; esac done

echo "argA $argA" echo "argB $argB" echo "argC $argC"

And some examples:

user@pc:/tmp$ ./test.bash -a
##-a##
##--##
####
####
####
#########
-a
--
--------
--

###--### break _________ argA 1 argB argC user@pc:/tmp$ ./test.bash -b 111 ##-b## ##111## ##--##

######### -b 111


--


###--### break _________ argA 0 argB 111 argC user@pc:/tmp$ ./test.bash -a -b 111 ##-a## ##-b## ##111## ##--##

######### -a

-b

-b 111


--


###--### break _________ argA 1 argB 111 argC user@pc:/tmp$ ./test.bash -b 111 -a ##-b## ##111## ##-a## ##--##

######### -b 111


-a


###-a### break _________ argA 0 argB 111 argC

3 Answers3

1
for arg
do
    ...
    shift 1

I don't think the shift here works like you'd like it to, the words the loop will loop over will be set when the loop starts, and a shift inside it doesn't affect that. E.g.

$ set -- aa bb cc; 
$ for x; do echo $x; shift; done
aa
bb
cc

"$@" will be left empty after the loop, though, since it was shifted once for each element.


Most of the examples using getopt use a while true loop instead, look into $1 and $2, shift manually and end the loop when seeing the -- terminator (which the util-linux getopt always adds, but you could check manually for non-options too):

See e.g.

ilkkachu
  • 138,973
0

You do not need to do a shift for the value. It is done automatically already. The shift 2 actually hides the next option.

Standard way to write a case is like this:

for arg
do
    echo $1
    echo $2
    echo "--------"
    case "$arg" in
        --arg-a | -a)
            argA=1
            ;;
        --arg-b | -b)
            argB=$2
            ;;
        --arg-c | -c)
            argC=$2
            ;;
    esac
    shift
done

Notice the single shift after the case.

Also, you should not put * in cases like that - it will react to any option. If you need a custom reaction to an unexpected option it is better to do something like:

opts=$(getopt ... )
[ $? -eq 0 ] || { 
    echo "Known options are..."
    exit 1
}
White Owl
  • 5,129
  • I don't think you can sensibly use $1 and $2 inside for arg; do like that, they'll just have whatever values they had before the loop. E.g. set -- aa bb cc; for x; do echo $1; done gives aa three times. Not that shift works either, set -- aa bb cc; for x; do echo $x; shift; done gives aa, bb and cc, without the shift affecting the loop. – ilkkachu Mar 21 '22 at 13:52
0

As say @ikkachu the bug is using for instead while, thanks.

And this is the tiny script that it working with getopt:

#! /bin/bash

#~ get_opt.example.sh #~ Copyright (C) 2022 Miguel de Dios Matias

#~ This program is free software: you can redistribute it and/or modify #~ it under the terms of the GNU General Public License as published by #~ the Free Software Foundation, either version 3 of the License, or #~ (at your option) any later version.

#~ This program is distributed in the hope that it will be useful, #~ but WITHOUT ANY WARRANTY; without even the implied warranty of #~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #~ GNU General Public License for more details.

#~ You should have received a copy of the GNU General Public License #~ along with this program. If not, see <http://www.gnu.org/licenses/>.

: ' Examples the calls:

$ ./getopt.example.bash --arg-b 111 -a 2222 3333 argA 1 argB 111 argC argD 0 unamedOptions 2222 3333

'

function help() { echo "$0 [(--arg-a | -a)] [(--arg-b | -b) <data_b>] [(--arg-c | -c <data_c>)] [-d] [(--help | -h)]" }

LONG_OPTION_LIST=( "arg-a" "arg-b:" "arg-c:" "help" ) SORT_OPTION_LIST=( "a" "b:" "c:" "d" "h" )

Read the parameters

opts=$(getopt -q
--longoptions "$(printf "%s," "${LONG_OPTION_LIST[@]}")"
--name "$(basename "$0")"
--options "$(printf "%s" "${SORT_OPTION_LIST[@]}")"
-- "$@" ) eval set -- "$opts"

argA=0 argD=0 unamedOptions=()

It it is same a queue (process the head) because $1 and $2

while true do case "$1" in --arg-a | -a) argA=1 ;; --arg-b | -b) argB=$2 shift 1 ;; --arg-c | -c) argC=$2 shift 1 ;; -d) argD=1 ;; --help | -h) help exit 0 ;; --) # End options now the unamed options ;; *) unamedOptions+=("$1") ;; esac shift 1 if [ $# -eq 0 ] then break fi done

echo "argA $argA" echo "argB $argB" echo "argC $argC" echo "argD $argD" echo "unamedOptions ${unamedOptions[@]}"