bashif-statement

Groups of compound conditions in Bash test


I want to have some groups of conditions in a Bash if statement. Specifically, I'm looking for something like the following:

if <myCondition1 and myCondition2> or <myCondition3 and myCondition4> then...

How may I group the conditions together in the way I describe for use with one if statement in Bash?


Solution

  • Use the && (and) and || (or) operators:

    if [[ expression ]] && [[ expression ]] || [[ expression ]] ; then
    

    They can also be used within a single [[ ]]:

    if [[ expression && expression || expression ]] ; then
    

    And, finally, you can group them to ensure order of evaluation:

    if [[ expression && ( expression || expression ) ]] ; then
    

    Warning: Be very careful when rewriting your normal if-statements into grouped expressions. The expressions within [[ ]] will NOT behave like normal Bash if-statements.

    The test, [ ], [[ ]] and (( )) operators evaluate expressions. They do not check the return-status of functions. In fact, they don't even execute functions at all. So you cannot perform function calls within expressions.

    For example, if you have a function foo() which ends in return 1 (the "failure" status code), you may be familiar with the normal Bash syntax for checking such a function:

    if foo; then
    

    But that will not work inside [[ ]] expressions. You CANNOT do the following:

    if [[ expression && ( expression || foo ) ]]
    

    That statement would break, since the foo call in the expression will NEVER run and will ALWAYS be treated as "true" (as if it returned 0).

    There are some unsafe workarounds, such as adding an explicit sub-shell call to a function and a value check via "$(foo)" -eq "0", but that won't really work either, because that will compare the "echoed" output of the function call, NOT its return-status code. And echoing your "status codes" is very unsafe, since it would be very difficult to cover every code execution path to catch all scenarios if you use that method. That workaround is further complicated by the fact that Bash treats all non-numeric values as "0" (for example, [[ "yeah" -eq "0" ]] && echo "hi" will echo "hi", since Bash acts as if any non-numeric or invalid-numeric string is equal to zero). You could possibly come up with some ugly, hacky workarounds via wrappers and conversion of return values into custom non-numeric strings, but it's never going to be a great solution.

    So unfortunately, you cannot and should not group if-statements that rely on Bash function calls. You'll have to do those separately, the correct way, with if foo; then, or by placing them outside the grouped [[ ]] expressions, such as via if foo && [[ expression || ( expression && expression ) ]].