2

I am fairly new to bash scripting and am struggling with some if-statement syntax.

I have currently written up the following loop:

for (( i = 2; i < $# - 1; i++)); do
    if [ $i -ne 0]; then
        if [ $i -ne 1]; then
            echo "$i was not 1 or 0. Please correct this then try again."
            exit 1;
        fi
    fi
done

This code is supposed to test whether any arguments after the first are either a 1 or a 0.

While the following errors are printed:

./blink.sh: line 36: [: missing `]'
./blink.sh: line 36: [: missing `]'

...the code actually runs fine afterwards (so the errors don't kill the program).

My understanding, however, is that in bash, you put spaces before and after the expression inside the if statement. So this:

if [ $i -ne 0]; then

Becomes:

if [ $i -ne 0 ]; then

However, running this code produces the following:

2 was not 1 or 0. Please correct this then try again.

The main issue I am having with this stems from not understanding how to indirectly reference the positional arguments provided by the execution command. As such, I am confused as to what syntax must be altered to call the objects the arguments point to (in this case, hopefully either a 1 or a 0) rather than the position of the arguments themselves (argument 1, 2, 3...).

Thanks!

EDIT: Altering the question to better fit the advice @randomir provided and clear up what the actual question entails

c4llmeco4ch
  • 311
  • 2
  • 11
  • 2
    `2` is indeed not `1`, nor is it `0`, so your code appears to be doing exactly what it says it's doing. If you want it to do something else, you need to give us some slight hint as to what that *something else* actually is. – jasonharper Oct 01 '17 at 00:45
  • You are looping from 2 to #$. You are never actually accessing the script arguments. `$i` just contains the number, 2, 3, 4. – Mort Oct 01 '17 at 00:59
  • `you put spaces before and after the expression inside the if statement.` There is no "inside" the if statement. `[` is *not* part of the grammar. The syntax is `if cmd; then ...` When you execute the command `[`, it must have `]` as its final argument. Much confusion will be avoided if you use `test` instead and write `if test "$i" -ne 0; then` – William Pursell Oct 01 '17 at 01:47
  • @chepner, you closed this question as a duplicate, but the other question covers only a fraction of this one. The real problem OP has was not with spacing (that's only intro), but with indirect references to positional arguments. Should we (or OP) maybe rephrase this question instead? – randomir Oct 01 '17 at 16:25
  • @randomir I added an edit (see the bold text) that hopefully better encapsulates the issue as per your comment. If there are any problems, let me know and I can edit again or someone else can edit it if that just makes things easier. Thanks again for your help. – c4llmeco4ch Oct 02 '17 at 08:03
  • @randomir I'm not concerned with multiple errors. The duplicate answers the immediate question, which could have easily been fixed had the OP done a little research (thereby exposing any less trivial errors). – chepner Oct 02 '17 at 12:02

1 Answers1

1

Based on:

This code is supposed to test whether any arguments after the first are either a 1 or a 0.

I'm assuming you're trying to access positional arguments $2, $3, etc. To make your for loop solution work, you would have to use an indirect reference: ${!i} (see shell parameter expansion). For example, this should work:

#!/bin/bash
for (( i = 2; i <= $#; i++ )); do
    if [[ ${!i} -ne 0 ]]; then
        if [[ ${!i} -ne 1 ]]; then
            echo "$i was not 1 or 0. Please correct this then try again."
            exit 1;
        fi
    fi
done

Note the i running from 2 to number of arguments $#. Also, note the use of recommended and less error-prone [[ .. ]] instead of [ .. ] (otherwise you would have to write [ "${!i}" -ne 0 ], etc).

A simpler solution which avoids the unnecessary indirect referencing looks like this:

#!/bin/bash
while [[ $2 ]]; do
    if (( $2 != 0 && $2 != 1 )); then
        echo "$2 is neither 0, nor 1"
        exit 1
    fi
    shift
done

We start checking the second argument ($2), use the arithmetic expression (( expr )) testing of value of the second argument, and shift positional arguments to the left by 1 at each iteration (now $3 becomes $2, etc).

randomir
  • 17,989
  • 1
  • 40
  • 55