Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to combine AND and OR condition in bash script for if condition?

I was trying to combine logical AND & OR in a bash script within if condition. Somehow I am not getting the desired output and it is hard to troubleshoot. I am trying to validate the input parameters passed to a shell script for no parameter and the first parameter passed is valid or not.

if [ "$#" -ne 1 ] && ([ "$1" == "ABC" ] || [ "$1" == "DEF" ] || [ "$1" == "GHI" ] || [ "$1" == "JKL" ]) 
then
echo "Usage: ./myscript.sh [ABC | DEF | GHI | JKL]"
exit 1
fi

Can anyone point out what is going wrong here?

like image 677
sijo0703 Avatar asked Jun 28 '16 05:06

sijo0703


People also ask

Can you use && in Bash?

The Bash logical (&&) operator is one of the most useful commands that can be used in multiple ways, like you can use in the conditional statement or execute multiple commands simultaneously.

What is && and || in shell script?

The operators "&&" and "||" shall have equal precedence and shall be evaluated with left associativity. For example, both of the following commands write solely bar to standard output: $ false && echo foo || echo bar $ true || echo foo && echo bar.

What does && do in Bash?

"&&" is used to chain commands together, such that the next command is run if and only if the preceding command exited without errors (or, more accurately, exits with a return code of 0). "\" by itself at the end of a line is a means of concatenating lines together.


3 Answers

The immediate problem with your statement is one of logic: you probably meant to write:

if [ "$#" -ne 1 ] || ! ([ "$1" = "ABC" ] || [ "$1" = "DEF" ] || [ "$1" = "GHI" ] || [ "$1" = "JKL" ]) 
then
  echo "Usage: ./myscript.sh [ABC | DEF | GHI | JKL]" >&2
  exit 1
fi

That is: abort, if either more than 1 argument is given OR if the single argument given does NOT equal one of the acceptable values.

Note the ! to negate the expression in parentheses and the use of the POSIX-compliant form of the string equality operator, = (rather than ==).

However, given that you're using Bash, you can make do with a single [[ ... ]] conditional and Bash's regular-expression matching operator, =~:

if [[ $# -ne 1 || ! $1 =~ ^(ABC|DEF|GHI|JKL)$ ]] 
then
  echo "Usage: ./myscript.sh [ABC | DEF | GHI | JKL]" >&2
  exit 1
fi

If POSIX compliance is not required, [[ ... ]] is preferable to [ ... ] for a variety of reasons. In the case at hand, $# and $1 didn't need quoting, and || could be used inside the conditional.

Note that =~ as used above works in Bash 3.2+, whereas the implicit extglob syntax used in anubhava's helpful answer requires Bash 4.1+;
in earlier versions you can, however, explicitly enable (and restore to its original value after) the extglob shell option: shopt -s extglob.

like image 85
mklement0 Avatar answered Oct 20 '22 01:10

mklement0


BASH actually allows use of extended glob inside [[ ... ]] and have && inside as well.

So you can do:

if [[ $# -ne 1 && $1 == @(ABC|DEF|GHI|JKL) ]]; then
   echo "Usage: ./myscript.sh [ABC | DEF | GHI | JKL]"
   exit 1
fi
like image 32
anubhava Avatar answered Oct 19 '22 23:10

anubhava


A few things:

  • [...] in bash is equivalent to the same test command (check the man page), so those && and || are not logical operators, but rather the shell equivalents
  • Parentheses in POSIX shell are not a grouping operator. They will work here, but they open a subshell, you are better off using standard test options of -a and -o (making your if statement if [ "$#" -ne 1 -a \( "$1" == "ABC" -o "$1" == "DEF" -o "$1" == "GHI" -o "$1" == "JKL" \) ], though based on your logic, it sounds like you actually want something like if [ "$#" -ne 1 -o \( "$1" != "ABC" -a "$1" != "DEF" -a "$1" != "GHI" -a "$1" != "JKL" \) ]. You probably can get better results with a case statement like follows:

usage() {
    echo "Usage: ./myscript.sh [ABC | DEF | GHI | JKL]"
}


if [ "$#" -ne 1 ] 
then
    usage
    exit 1
fi

case "$1" in
    ABC)
        echo "Found ABC"
        ;;
    DEF)
        echo "Found DEF"
        ;;
    GHI)
        echo "Found GHI"
        ;;
    JKL)
        echo "Found JKL"
        ;;
    *)
        usage
        exit 1
        ;;
esac

If you want to pass a set of possible static arguments in, you might want to look at the getopts special shell command.

like image 32
Taywee Avatar answered Oct 20 '22 00:10

Taywee