user2153627
user2153627

Reputation:

Sh - Get next array element

I am getting twice the arguments used with my script :

The first time using this following code, to make sure there isn't arguments that I don't want (I only take in consideration arg1:arg2:arg3)

     PARAM=$@
     while [ "$#" -gt "0" ]; do
              case $1 in
                       arg1)
                                shift2
                                ;;
                       arg2)
                                shift
                                ;;
                       arg3)
                                shift
                                ;;
                       *)
                                echo "Usage : ./test.sh arg1 <VAL1> <VAL2> [arg2] [arg3]"
                                exit 2
                                ;;
              esac
              shift
     done

And I would like to parse those arguments all over again and be able able to get the two following arguments when I get arg1, so I started with something like this :

     for command in $PARAM
     do
         case $command in
             arg1)
                 shift
                 VALUE1=$command
                 shift
                 VALUE2=$command
                 exec_arg1
                 ;;
         esac
         shift

But while using "shift", I get the error shift: can't shift that many

The shebang is "#!/bin/sh" and I am looking for a solution without having to use bash's shebang (i.e. "#!/bin/bash")

Upvotes: 1

Views: 725

Answers (1)

ormaaj
ormaaj

Reputation: 6577

You cannot concatenate a list to a string and safely turn it back into a list (without a complex helper function). See this FAQ.

shift behaves differently in different shells. In Bash and Zsh, when no positional parameters remain, shift simply returns false. Many shells including Dash (contrary to POSIX, perhaps because of ksh behavior) throw a fatal error instead. ksh93 provides a way around this via the command builtin, but this also appears to be not specified by POSIX, though this workaroud also works in Dash anyway, but not mksh (there's also a bug in mksh that I just discovered yesterday that prevents this from working, and the maintainer probably won't fix it in a way that allows the workaround). Busybox doesn't follow POSIX either and gives no meaningful exit code.

Anyway, the point is you can't rely upon shift because so many shells have bugs here. You should iterate arguments without shifting them if possible and test the number of remaining arguments in a way that ensures you aren't shifting over the edge like you're currently doing. I don't think your idea of pre-validating the arguments is better than just validating and parsing at the same time.

#!/bin/sh

f()
    if ! ${_called_f+false}; then
        while ! ${1+false}; do
            case $1 in
                arg1)
                    ${3+:} return 1
                    value1=$2 value2=$3
                    shift 3
                    exec_arg1
                    ;;
                arg[23])
                    ${2+:} return 1
                    value1=$1
                    shift
                    "exec_arg${value1##"${value1%?}"}" # Only do this crap if restricted to POSIX
                    ;;
                *)
                    return 1
            esac
        done
    else
        # Hack nobody will understand since this probably isn't what you want anyway.
        _called_f= value1= value2= command eval \
            'typeset +x value{1,2} 2>/dev/null; f "$@"'
    fi

if ! f "$@"; then
    echo 'Error parsing args, exiting...' >&2
    exit 1
fi

See also: option parsing

Upvotes: 2

Related Questions