0

First: I'm fairly new to bash batch-scripting but have experience in other script and programming languages

Second: I already looked at Meaning of "[: too many arguments" error from if[] (square brackets) but this that was not the solution for my problem.

I'm creating a command-line parser in a bash script. My first attempt actually already works but still has some if-statement code-duplication in the main-loop which I want to solve, so I moved it to the actual parsing-function and then I run into a "[: too many arguments" error which I cannot get solved (adding "" around variables did not work) in a piece of code which does work if I do not move the if.

Note: in the actual code there is one more command-line option, I left that out for 2 reasons:

  1. shorter listing
  2. error is seen on all options

The working code looks like this:

    arg_str = $@
    args=("$@")
    arglen=$#

    target=some_filename.txt
    in_dir=/some_input_dir/

    function working_parser()
    {
        local option=$1
        myresult=''

        i=0

        while [ ${args[i]} != $option -a $i -n $arglen ]
        do
            i=$((i+1))
        done
        #TODO add check on $i>arglen
        myresult=${args[$((i+1))]}
    }

    if [[ $arg_str == *'-t'* ]]; then
        working_function '-t'
    fi
    if [[ $myresult != '' ]]; then
        target=$myresult
    fi


    if [[ $arg_str == *'-i'* ]]; then
        working_function '-i'
    fi
    if [[ $myresult != '' ]]; then
        in_dir=$myresult
    fi

The failing code looks like this (Left out the definitions, since they are the same as for the working code). The '[: too many arguments' error occurs in the while loop, where it did not happen in that same code in the working-version.

    function failing_parser()
    {
        local option=$1
        myresult=''

        if [[ $arg_str == *$option* ]]; then
            i=0

            while [ ${args[i]} != $option -a $i -n $arglen ]
            do
                i=$((i+1))
            done
            #TODO add check on $i>arglen
            myresult=${args[$((i+1))]}
        fi
    }

    failing_parser '-t'
    if [[ $myresult != '' ]]; then
        target=$myresult
    fi

    failing_parser '-i'   
    if [[ $myresult != '' ]]; then
        in_dir=$myresult
    fi

What am I doing wrong?

Nemelis
  • 4,858
  • 2
  • 17
  • 37
  • 1
    Quotes! `while [ "${args[i]}" != "$option" -a "$i" -n "$arglen" ]` – KamilCuk Aug 28 '19 at 09:54
  • I tried quotes and it does not work. Still keep getting the same issue. (Actually used the same line as Kamil suggests) /*edit*/ now even tried it with quotes around the variables in both the if and while loop – Nemelis Aug 28 '19 at 10:06
  • `-n` is unary, not a binary, operator; `"$i" -n "$arglen"` is an invalid expression. What are you actually trying to *do*? Note also that `-a` for logical AND is considered obsolete; use `[ ... ] && [ ... ]` instead, or use `[[ ... && ... ]]`. – chepner Aug 28 '19 at 12:56
  • Did you mean `-ne` instead of `-n`? – chepner Aug 28 '19 at 12:56
  • @Nemelis, if quoting doesn't fix your *whole* problem, that's still not a reason not to quote. The code is even more broken without quoting than it is *with* quoting, insofar as without quoting you have additional bugs that show up when your values don't string-split and glob to the expected number of words (presumably one per expansion, but unless you quote that isn't a guarantee). – Charles Duffy Aug 28 '19 at 13:03
  • @CharlesDuffy: I did not mean that it was not a good comment. Just that I tried it and it did not work. – Nemelis Aug 28 '19 at 13:13
  • @chepner: I tried to check if i is not equal to arglen. I now see the mistake (I even made it twice when making the code-copy for here: Somehow the 'e' from '-ne' was removed when upgrading the function. I will use your comment about &&. Like I said I'm not that experienced with bash scripting and then google is your friend ;-). – Nemelis Aug 28 '19 at 13:18

1 Answers1

2

There are too many arguments because -n is unary operator, not a binary operator. Perhaps you mean -ne?

while [ "${args[i]}" != "$option" -a "$i" -ne "$arglen" ]

-a is considered nonportable and obsolete; it's better to use two separate [ commands joined by && instead:

while [ "${args[i]}" != "$option" ] && [ "$i" -ne "$arglen" ]

However, since you are already using one bash-specific feature (arrays), you may as well use [[ ... ]] as well:

while [[ "${args[i]}" != "$option" && "$i" -ne "$arglen" ]]

or even an arithmetic command:

while [[ "${args[i]}" != "$option" ]] && (( i != arglen )); 
chepner
  • 497,756
  • 71
  • 530
  • 681
  • First saw your comment above and already reacted over there. Thank you for your help. – Nemelis Aug 28 '19 at 13:19
  • After I wrote the comment, I realized the assumption was probably safe enough to go ahead with an answer :) – chepner Aug 28 '19 at 13:25