40

I have a Bash shell function that takes an argument and performs something on it if needed.

do_something() {
  if [need to do something on $1]
  then
    do it
    return 0
  else
    return 1
  fi
}

I want to call this method with several arguments and check if at least one of them succeeded.

I tried something like:

if [ do_something "arg1" ||
     do_something "arg2" ||
     do_something "arg3" ]
then
  echo "OK"
else
  echo "NOT OK"
fi

Also, I want to make sure that even if the first condition is true all other conditions will still be evaluated.

What is the correct syntax for that?

Itay
  • 693

4 Answers4

64

Use backslashes.

if [ $(do_something "arg1") ] || \
   [ $(do_something "arg2") ] || \
   [ $(do_something "arg3") ]
then
  echo "OK"
else
  echo "NOT OK"
fi

EDIT

Also - I want to make sure that even if the first condition is true all other conditions will still be evaluated.

That's not possible in only one if statement. Instead you can use a for loop that iterates over the arguments and evaluates them separately. Something like:

do_something() {
  for x in "$@"
  do
    if [need to do something on $x]
    then
      do it
    else
      echo "no action required on $x"
    fi
  done
}
tectux
  • 981
  • thanks, tried it - I get an error [: do_something: unary operator expected – Itay May 11 '13 at 11:56
  • Did you put each evaluation between its own brackets? So [ expr1 ] || [ expr2 ] instead of [ expr1 || expr2 ]. – tectux May 11 '13 at 11:59
  • yes, I did :(... – Itay May 11 '13 at 12:01
  • My guess is that do_something "arg1" is interpreted as two arguments whereas expression should be unary. Any way of making do_something "arg1" be evaluated as one argument? – Itay May 11 '13 at 12:10
  • Ahh.... found it - apparently the right way to do it is: if do_something "arg1" || do_something "arg2". No need for the []. – Itay May 11 '13 at 12:12
  • 1
    You're right. This works too: if [ $(do_something "arg1") ]. – tectux May 11 '13 at 12:13
  • Note: A command not found stderr message appears in bash 5.0.3 if a # comment follows one of the backslashes. – baltakatei Jun 13 '20 at 13:24
9

Run the commands first, then check if at least one of them succeeded.

#!/bin/bash

success=0
do_something arg1 && success=1
do_something arg2 && success=1
do_something arg3 && success=1

if ((success)); then
    printf 'Success! At least one of the three commands succeeded\n'
fi
geirha
  • 47,348
  • 3
    This is confusing, I'll be glad if you could provide some explanation on how conditional statements work in bash..., do_something returns 0 upon success, but then we update success to 1..., if ((success)) evaluates to something different than if success. I am confused :) – Itay May 13 '13 at 16:14
  • 2
    @Itay ((...)) is an arithmetic command. If the result inside, is non-zero, it returns true (0). If the result inside is 0, it returns false (1). "success" is treated as a variable which it expects to contain a number. See http://mywiki.wooledge.org/ArithmeticExpression – geirha May 13 '13 at 17:37
  • Thanks, I guess that the part that confused me is that 0 is true and 1 is false usually it is the opposite :) – Itay May 13 '13 at 18:01
  • 2
    @Itay Yes, it may be confusing at first. Bash differs from general purpose languages in that it is "command based"; it doesn't call functions, it doesn't have classes and methods, it runs commands, and tests commands' exit statuses. And it is much easier to run commands in bash than languages like C, python, java, perl, etc.. Commands exit with 0 to signal success, and 1-255 for various types of failures. (A command can generally only succeed in one way, but fail for various different reasons) – geirha May 13 '13 at 18:34
  • 2
    This solution has the advantage that you can comment out the lines you don't want to deactivate them temporarily – Josiah Yoder Mar 12 '18 at 13:10
  • best to contain this logic in a function otherwise the success=0 variable will global? – Khalil Gharbaoui May 29 '20 at 02:33
8

The correct syntax is:

if  do_something "arg1" || \
    do_something "arg2" || \
    do_something "arg3"
then
  echo "OK"
else
  echo "NOT OK"
fi

\ is used to tell the shell a command continues in the next line.

EDIT: I think this should do what you want:

#!/bin/bash

do_something() {
  if [need to do something on $1]
  then
    do it
    echo "OK"
  else
    echo "NOT OK"
  fi
}

do_something "arg1"
do_something "arg2"
do_something "arg3"
Eric Carvalho
  • 55,573
0

I prefer:

if {
    do_something "arg1" ||
    do_something "arg2" ||
    do_something "arg3"
}; then
    echo "OK"
else
    echo "NOT OK"
fi

Toxiro
  • 109
  • You script won't work at all : after the 1st test parameter, it would generate a \n character, so your if statement would generate some errors, and do_something aren't command names, so more errors – damadam Dec 17 '19 at 13:32
  • For me it works in zsh and bash, IF do_something is a function of course. do_something must be function or command, what else should it be? Furthermore I do not see where it would generate a \n character except after echoing the result and this is expected... – Toxiro Dec 20 '19 at 10:11
  • @damadam Could you explain this or remove your downvote please? – Toxiro Dec 30 '19 at 11:18