10

I know how to compare two string in bash:

if [ "$build_type" = "devite" ]; then
  echo "building'"
fi

But what I need is to check if "$build_type" is in ["devite", "relite"]

so something similar to this:

if [ "$build_type" in ["devite", "relite"] ]; then
  echo "building'"
fi

Can anyone shed light on this?

Learner
  • 1,686
  • 4
  • 19
  • 38
  • 1
    `bash` doesn't have lists. The closest analogy would be to check if a string is a key in a given associative array. – chepner Jan 24 '20 at 13:49
  • @chepner how can I achieve the above like this: if "$build_type"="dev" or "$build_type"=rel? – Learner Jan 24 '20 at 13:52
  • Agreed, don't do this. If you happen to have a bash array variable and you want to know if it contains a particular piece of text, I would expand it to a string using printf, and then check that. – Gem Taylor Jan 24 '20 at 14:49
  • Similar to: https://stackoverflow.com/questions/3685970/check-if-a-bash-array-contains-a-value – Efren Jan 10 '23 at 07:13

4 Answers4

12

Join two test/[ commands with ||:

if [ "$build_type" = devite ] || [ "$build_type" = relite ]; then
  echo "building"
fi

or use a case statement.

case $build_type in
  devite|relite) echo "building" ;;
esac

If the targets are in an associative array, you can check for the existence of a key.

declare -A targets=([devite]= [relite]=)

if [[ -v targets[$build_type] ]]; then
    echo "building"
fi
chepner
  • 497,756
  • 71
  • 530
  • 681
  • I would propose to always use double quotes around a variable use (`"$build_type"` instead of `$build_type`). Who knows what's in that variable, maybe spaces? Then it would give weird errors in the `case` case. – Alfe Jan 24 '20 at 13:56
  • 1
    The expansion used for an array index isn't subject to pathname expansion or word-splitting. – chepner Jan 24 '20 at 14:03
  • But in the `case` case it is necessary (`case $build_type in`), isn't it? EDIT: doesn't seem to be, interesting. That sounds like a weird exception to me … I would still propose to use them, just because memorizing all the different cases can be error-prone so that in the end one might leave them away in a different case where they would have been necessary, and they never are wrong. To me a use without double quotes already looks like "I explicitly want the word split here in this case". – Alfe Jan 27 '20 at 13:19
  • 2
    `case`, as a special compound statement, doesn't have to follow the usual rules for evaluating a simple command. Since the purpose is to compare the value of `$build_type` to the patterns inside the statement, there is no reason to perform either word-splitting or pathname expansion on parameter expansions following the keyword `case`, which is the only reason you would have to quote it. – chepner Jan 27 '20 at 14:12
  • Yeah, I see the reasoning. I still would always use double quotes unless I really want a word splitting. But of course that's rather a personal opinion. – Alfe Jan 27 '20 at 15:48
7

I would use a case for this, hmm, case:

case "$build_type" in
  devite|relite)
    echo "building"
    ;;
esac

The pipe symbol (|) specifies an or logic.

Of course with the case shell builtin you can do much more like have several cases with different handling code, but it is the easiest to read thing for a fixed list of fixed simple strings (with special characters in the strings it might become a quoting nightmare).

If you really want to use a list array, I would use a loop:

names=( devite relite )
for name in "${names[@]}"
do
  if [ "$build_type" = "$name" ]
  then
    echo "building"
    break  # leave the loop
  fi
done
Alfe
  • 56,346
  • 20
  • 107
  • 159
3

It depends how you have the target strings. If they are in an array (eg a=(devite relite), you could do:

if [[ "${a[@]}" =~ $build_type ]]; then ...
William Pursell
  • 204,365
  • 48
  • 270
  • 300
1

Yet another option is to use arrays and grep:

#!/bin/bash
declare -a types=("devite" "relite")
type=devite
neg=dovite

if grep -q "${type}" <<< "${types[*]}" ; then
        echo "building"
else
        echo "not building"
fi

if grep -q "${neg}" <<< "${types[*]}" ; then
        echo "building"
else
        echo "not building"
fi
=> building
   not building

So many ways to skin that cat :)

Edit: If you cannot trust the input to be valid:

#!/bin/bash
declare -a types=("devite" "relite")
type=devite
neg="ite rel"

function j { local IFS=$'\n'; echo "$*"; }

if grep -q "${type}" <<< $(j "${types[@]}") ; then
        echo "building"
else
        echo "not building"
fi
if grep -q "${neg}" <<< $(j "${types[@]}") ; then
        echo "building"
else
        echo "not building"
fi
Tom Regner
  • 6,856
  • 4
  • 32
  • 47
  • 2
    A type like `ite rel` will cause a false positive. – chepner Jan 24 '20 at 14:08
  • @chepner well, unlikely in real life scenarios, I'd say - and avoidable by localy modifying IFS accordingly (see edit) - but anyway, this is just a pointer in a possible direction – Tom Regner Jan 24 '20 at 14:24
  • 1
    Why jump through those hoops when there are more efficient solutions (i.e., don't run external programs) that don't need to? – chepner Jan 24 '20 at 14:28