13

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?

4
  • 1
    bash doesn't have lists. The closest analogy would be to check if a string is a key in a given associative array. Commented Jan 24, 2020 at 13:49
  • @chepner how can I achieve the above like this: if "$build_type"="dev" or "$build_type"=rel? Commented Jan 24, 2020 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. Commented Jan 24, 2020 at 14:49
  • Similar to: stackoverflow.com/questions/3685970/… Commented Jan 10, 2023 at 7:13

4 Answers 4

15

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
Sign up to request clarification or add additional context in comments.

5 Comments

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.
The expansion used for an array index isn't subject to pathname expansion or word-splitting.
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".
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.
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.
8

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

Comments

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 ...

1 Comment

That's also brittle. This will match if build_type is something like ite rel.
2

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

3 Comments

A type like ite rel will cause a false positive.
@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
Why jump through those hoops when there are more efficient solutions (i.e., don't run external programs) that don't need to?

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.