In Bash, what is the simplest way to test if an array contains a certain value?
The OP added the following answer themselves, with the commentary:
With help from the answers and the comments, after some testing, I came up with this:
function contains() {
local n=$#
local value=${!n}
for ((i=1;i < $#;i++)) {
if [ "${!i}" == "${value}" ]; then
echo "y"
return 0
fi
}
echo "n"
return 1
}
A=("one" "two" "three four")
if [ $(contains "${A[@]}" "one") == "y" ]; then
echo "contains one"
fi
if [ $(contains "${A[@]}" "three") == "y" ]; then
echo "contains three"
fi
Below is a small function for achieving this. The search string is the first argument and the rest are the array elements:
containsElement () {
local e match="$1"
shift
for e; do [[ "$e" == "$match" ]] && return 0; done
return 1
}
A test run of that function could look like:
$ array=("something to search for" "a string" "test2000")
$ containsElement "a string" "${array[@]}"
$ echo $?
0
$ containsElement "blaha" "${array[@]}"
$ echo $?
1
grep
and printf
Format each array member on a new line, then grep
the lines.
if printf '%s\n' "${array[@]}" | grep -x -q "search string"; then echo true; else echo false; fi
example:
$ array=("word", "two words")
$ if printf '%s\n' "${array[@]}" | grep -x -q "two words"; then echo true; else echo false; fi
true
Note that this has no problems with delimeters and spaces.
if ( dlm=$'\x1F' ; IFS="$dlm" ; [[ "$dlm${array[*]}$dlm" == *"$dlm${item}$dlm"* ]] ) ; then
echo "array contains '$item'"
else
echo "array does not contain '$item'"
fi
This approach uses neither external utilities like grep
nor loops.
What happens here, is:
IFS
variable value;IFS
value replacement temporary by evaluating our conditional expression in a sub-shell (inside a pair of parentheses)I generally write these kind of utilities to operate on the name of the variable, rather than the variable value, primarily because bash can't otherwise pass variables by reference.
Here's a version that works with the name of the array:
function array_contains # array value
{
[[ -n "$1" && -n "$2" ]] || {
echo "usage: array_contains <array> <value>"
echo "Returns 0 if array contains value, 1 otherwise"
return 2
}
eval 'local values=("${'$1'[@]}")'
local element
for element in "${values[@]}"; do
[[ "$element" == "$2" ]] && return 0
done
return 1
}
With this, the question example becomes:
array_contains A "one" && echo "contains one"
etc.
Here is a small contribution :
array=(word "two words" words)
search_string="two"
match=$(echo "${array[@]:0}" | grep -o $search_string)
[[ ! -z $match ]] && echo "found !"
Note: this way doesn't distinguish the case "two words" but this is not required in the question.