How to combine AND and OR condition in bash script for if condition?
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 1fi
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 1fi
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
.
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 1fi
A few things:
[...]
in bash is equivalent to the sametest
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 statementif [ "$#" -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 likeif [ "$#" -ne 1 -o \( "$1" != "ABC" -a "$1" != "DEF" -a "$1" != "GHI" -a "$1" != "JKL" \) ]
. You probably can get better results with acase
statement like follows:
usage() { echo "Usage: ./myscript.sh [ABC | DEF | GHI | JKL]"}if [ "$#" -ne 1 ] then usage exit 1ficase "$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.