在Bash中,测试数组是否包含某个值的最简单方法是什么?


当前回答

结合Beorn Harris和loentar的回答,我们得出了一个更有趣的单行测试:

delim=$'\x1F' # define a control code to be used as more or less reliable delimiter
if [[ "${delim}${array[@]}${delim}" =~ "${delim}a string to test${delim}" ]]; then
    echo "contains 'a string to test'"
fi

它不使用额外的函数,不替换测试,并添加了额外的保护,防止使用控制代码作为分隔符偶尔出现错误匹配。


UPD:感谢@ChrisCogdon的注意,这个错误的代码被重写并以https://stackoverflow.com/a/58527681/972463的形式发布。

其他回答

$ myarray=(one two three)
$ case "${myarray[@]}" in  *"two"*) echo "found" ;; esac
found

如果您想做一个快速而复杂的测试,看看是否值得遍历整个数组以获得精确匹配,Bash可以像对待标量一样对待数组。测试标量中的匹配项,如果没有,则跳过循环节省时间。显然你会得到假阳性。

array=(word "two words" words)
if [[ ${array[@]} =~ words ]]
then
    echo "Checking"
    for element in "${array[@]}"
    do
        if [[ $element == "words" ]]
        then
            echo "Match"
        fi
    done
fi

这将输出“Checking”和“Match”。使用array=(word "two words" something),它只会输出"Checking"。使用array=(单词“two widgets”什么的)将没有输出。

这是一个小小的贡献:

array=(word "two words" words)  
search_string="two"  
match=$(echo "${array[@]:0}" | grep -o $search_string)  
[[ ! -z $match ]] && echo "found !"  

注意:这种方法不区分大小写“两个单词”,但在问题中不需要这样做。

OP自己添加了以下答案,并附上了评论:

在回答和评论的帮助下,经过一些测试,我得出了这个结论:

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

我看到了一些解决这个问题的方法。

对于使用grep的索引数组

grep ${value} <<< ${array[*]} && true || false

对于使用grep的关联数组键

grep ${value} <<< "${!array[*]}" && true || false

我们可以使用awk,但它可能太过了。

awk --assign "v=${value}" '$v~$0 {print true}' <<<"${!array[*]}

Case语句。

case "${array[*]}" in (*${value}*) true ;; (*) false ;; esac

Bash条件表达式在ksh88风格双方括号:

[[ ${array[@]} =~ ${value} ]] && true || false

注意:顺序很重要,正则表达式在=~ match运算符的右边。

Bash for循环

for ((i=0;i<"${#array[*]}";i++)) ; [[ ${array[i]} = $value ]] && break 0 &> /dev/null || continue; done

注意,在这种特殊情况下,真逻辑是颠倒的,即1=真,0=假。这是因为我们使用break 0强制break内置除true之外的退出代码,除非break n参数小于1,否则总是如此。我们必须要打破循环,我们想要一个布尔退出码除了默认的'true',所以在这种情况下,我们翻转了逻辑。因此,使用具有返回true语义的函数可能更有意义。