0

I faced some issue, where OR operator doesn't work for $1. Eg:

if [[ "$1" != '1' || '2' ]]
        then
               echo "BAD"
        else
               echo "OK"
        fi

When I run this test no matter what $1 is, BAD always appears.

How can I tie $1 to be 1 or 2 only?

  • This is a very common error. I see it a lot with beginner programmers. However it is not how boolean logic works. Imagine putting in brackets. Where could you put them to make it work? Note there are some languages that you can ask if $1 is not in {'1', '2' } then … (see case example in bash). – ctrl-alt-delor Feb 8 '18 at 12:47
5

With standard sh syntax (so not only recognised by bash but all other POSIX compatible shells):

case $1 in
  (1 | 2) echo OK;;
  (*) echo BAD;;
esac

Or:

if [ "$1" = 1 ] || [ "$1" = 2 ]; then
  echo OK
else
  echo BAD
fi

(note that it's a byte-to-byte comparison of strings. 01, 1e0, 1.0, 20/20, 0x1 would also be considered as BAD even though numerically they could be regarded as being the same as 1).

Note that the =/!= operators in bash's [[...]] construct (copied from ksh) are actually pattern matching operators as opposed to equality operators, so in this special case where you want to match a single character, you could also use [[ $1 != [12] ]] like you could do case $1 in ([12]) in standard sh syntax.

  • Magnificent idea with case! Dunno how didn't i use it myself ) – faceless Feb 8 '18 at 12:42
4

The reason you always go into the BAD branch of the if statement is that the test

[[ "$1" != '1' || '2' ]]

first tests whether $1 is not 1, and if it is not, BAD is printed. If it is, 2 is evaluated as a test. A string like 2 is always true, and BAD is printed.

Instead:

if [ "$1" != '1' ] && [ "$1" != '2' ]; then

or, with arithmetic tests,

if [ "$1" -ne 1 ] && [ "$1" -ne 2 ]; then

or, using arithmetic evaluation,

if (( $1 != 1 )) && (( $1 != 2 )); then
1
if [[ "$1" != '1' || "$1" != '2' ]]
then
               echo "BAD"
else
               echo "OK"
fi

Then != statement needs to be repeated for the not equal to 2 as well as 1

  • The result is the same. – faceless Feb 8 '18 at 11:59
1

The logic of ($1 != 1) OR ($1 != 2) is flawed.

If $1 is 1 then it is not equal to 2 so the test will be true.
Also, if $1 is 2 then it is not equal to 1 so the test will be also true. In short, the test will always be true.

You should test equality: ($1 == 1) or ($1 == 2) like this:

if [[ ($1 == '1') || ($1 == '2') ]]; then
    echo "$1 OK"
else
    echo "$1 BAD"
fi

Or simpler (in ksh, bash or zsh):

[[ $1 == [12] ]] && echo "$1 OK" || echo "$1 BAD"

Or using sh syntax (portable to all POSIX shells):

if [ "$1" = '1' ] || [ "$1" = '2' ]; then
            echo "$1 OK"
else
            echo "$1 BAD"
fi

Or with a case idiom:

case $1 in
    (1 | 2) echo "$1 OK"  ;;
    (*) echo BAD "$1 BAD" ;;
esac

All the above is a string comparison, if you want a numeric comparison, you may do:

if [ "$1" -eq 1 ] || [ "$1" -eq 2 ]; then
            echo "$1 OK"
else
            echo "$1 BAD"
fi

However, that will not work if $1 is 1.0 or 1e0 or many other numerical equivalent to 1. Will work with 01 or 0001 though.

  • 2
    Note that what is supported in [ "$1" -eq 1 ] varies from shell/[ to shell/[ (and is an arbitrary command injection in some if $1 comes from an untrusted source). With ksh93, that would return true for 1.0 or 1e0 or 20/20 and sometimes with RANDOM%2 and reboot with 'a[$(reboot)]'. – Stéphane Chazelas Feb 8 '18 at 19:15

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.