检查Bash数组是否包含值

Pao*_*sco 387 arrays bash

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

编辑:在答案和评论的帮助下,经过一些测试后,我想出了这个:

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
Run Code Online (Sandbox Code Playgroud)

我不确定它是否是最佳解决方案,但似乎有效.

Kee*_*gan 380

这种方法的优点是不需要遍历所有元素(至少没有明确地).但是因为array_to_string_internal()array.c中仍然循环遍历数组元素并将它们连接成一个字符串,所以它可能不比提出的循环解决方案更有效,但它更具可读性.

if [[ " ${array[@]} " =~ " ${value} " ]]; then
    # whatever you want to do when arr contains value
fi

if [[ ! " ${array[@]} " =~ " ${value} " ]]; then
    # whatever you want to do when arr doesn't contain value
fi
Run Code Online (Sandbox Code Playgroud)

请注意,如果要搜索的值是带空格的数组元素中的单词之一,则会产生误报.例如

array=("Jack Brown")
value="Jack"
Run Code Online (Sandbox Code Playgroud)

正则表达式将Jack被视为在数组中,即使它不是.因此,IFS如果您仍想使用此解决方案,则必须更改正则表达式中的分隔符,如下所示

IFS=$'\t'
array=("Jack Brown\tJack Smith")
unset IFS

value="Jack Smith"

if [[ "\t${array[@]}\t" =~ "\t${value}\t" ]]; then
    echo "yep, it's there"
fi
Run Code Online (Sandbox Code Playgroud)

  • Oneliner:`[["$ {branches [@]}"=〜"$ {value}"]] && echo"YES"|| 回声"不";` (8认同)
  • **明白了** 这意外地匹配了作为其他文件名的子字符串的文件名。例如,如果您的干草堆包含“.header”并且您的针是“ad”,那么即使文件名中没有空格,ad也会匹配。 (6认同)
  • Shellcheck 抱怨这个解决方案,SC2199 和 SC2076。我无法在不破坏功能的情况下修复警告。除了禁用该行的 shellcheck 之外,还有其他想法吗? (5认同)
  • [SC2076](https://github.com/koalaman/shellcheck/wiki/SC2076) 很容易修复,只需删除`if`中的双引号。我认为没有办法通过这种方法避免 [SC2199](https://github.com/koalaman/shellcheck/wiki/SC2199)。您必须显式循环数组,如其他一些解决方案所示,或 [忽略](https://github.com/koalaman/shellcheck/wiki/Ignore) 警告。 (5认同)
  • @JoshHabdas如果发生这种情况,您可能忘记了引号中值周围的空格(或者您可能正在使用与 Bash 具有不同行为的其他 shell)。我使用 Bash 4.4.12 测试了您的场景,但它不匹配。 (3认同)
  • 我在第一个正则表达式值匹配的开头添加了一个空格,以便它只匹配单词,而不匹配以单词结尾的内容。效果很好。但是,我不明白您为什么使用第二个条件,第一个单独工作不能正常工作吗? (2认同)
  • 为什么需要用空格或制表符包围匹配的单词? (2认同)

小智 372

以下是实现此目的的一个小功能.搜索字符串是第一个参数,其余是数组元素:

containsElement () {
  local e match="$1"
  shift
  for e; do [[ "$e" == "$match" ]] && return 0; done
  return 1
}
Run Code Online (Sandbox Code Playgroud)

该函数的测试运行可能如下所示:

$ array=("something to search for" "a string" "test2000")
$ containsElement "a string" "${array[@]}"
$ echo $?
0
$ containsElement "blaha" "${array[@]}"
$ echo $?
1
Run Code Online (Sandbox Code Playgroud)

  • 尼斯.我将其称为elementIn(),因为它检查第一个参数是否包含在第二个参数中.containsElements()听起来像数组会先行.对于像我这样的新手,如何在"if"语句中使用不写入stdout的函数的例子会有所帮助:`if elementIn"$ table""$ {skip_tables [@]}"; echo echo skipping table:$ {table}; fi;`谢谢你的帮助! (23认同)
  • @Stelios`shift`将参数列表向左移动(删除第一个参数),而没有`in`的`for`隐式迭代参数列表. (8认同)
  • 很好用!我只需要记住像引号一样传递数组:`"$ {array [@]}"`.否则包含空格的元素将破坏功能. (5认同)
  • @Bluz && construct是一个布尔AND运算符.布尔运算符的使用创建了一个布尔语句.布尔逻辑表示如果&&评估为true之前和之后的语句,则整个语句只能为true.这被用作insted和if block的快捷方式.评估测试,如果为false,则无需评估返回,因为一旦测试失败并且因此未运行,它与整个语句无关.如果测试成功,那么布尔语句的成功要求确定返回的结果,以便运行代码. (5认同)
  • @James按惯例,bash中的成功代码为"0",错误是一切> = 1.这就是成功时返回0的原因.:) (4认同)
  • 那里有0/1约定,所以你可以这样做:`if containsElement"a string""$ {array [@]}"; 然后回声"发现它"; 否则回声"没找到它"; fi` (3认同)
  • 代码就像一个魅力,它简单而优雅。然而,我不明白一件事。我在 for 循环之前执行“echo $e”,它看起来像预期的那样是空的。for 循环如何知道它应该遍历 $1 是移位操作后的数组?它是否应该从无处读取 $1 参数?我在这里缺少什么? (3认同)
  • 我一直试图绕过“ local e match = $ 1 shift”和“ for e”,但是仍然不明白它是如何工作的。 (2认同)
  • 尽管@Christian发表了评论,我仍然不明白“e”应该如何包含位置参数索引&gt; 1。我知道 `shift` 的作用,但实际上没有任何东西通过 `$@` 循环来将每个参数添加到 `$e` 中。另外,该功能似乎不适用于 bash v5.1 (2认同)
  • 如果没有指定“in”部分,@ErinSchoonover“for”会隐式循环“$@”。是的,我认为这是一个有问题的语法捷径,但事实就是如此。 (2认同)
  • @克里斯蒂安谢谢你!设置调试模式(`set -x`)后通过 shell 运行它,我可以看到 `for` 正在执行您所说的操作:`- for e in "$@"` (2认同)

gho*_*g74 59

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

  • 请注意,这不会分别迭代数组中的每个元素...而是简单地连接数组并将"two"作为子字符串匹配.如果正在测试确切的单词"two"是否是数组中的元素,这可能会导致不良行为. (64认同)
  • 错误!原因:```case"$ {myarray [@]}"in*"t"*)echo"found";; esac```输出:`找到` (13认同)

Sco*_*ott 41

for i in "${array[@]}"
do
    if [ "$i" -eq "$yourValue" ] ; then
        echo "Found"
    fi
done
Run Code Online (Sandbox Code Playgroud)

对于字符串:

for i in "${array[@]}"
do
    if [ "$i" == "$yourValue" ] ; then
        echo "Found"
    fi
done
Run Code Online (Sandbox Code Playgroud)


Jel*_*Cat 25

一线解决方案

printf '%s\n' ${myarray[@]} | grep -P '^mypattern$'
Run Code Online (Sandbox Code Playgroud)

说明

printf语句在单独的行上打印数组的每个元素.

grep语句使用特殊字符^,并$找到包含一行正好给出的模式mypattern(无多,不会少).


用法

把它写成一个if ... then声明:

if printf '%s\n' ${myarray[@]} | grep -q -P '^mypattern$'; then
    # ...
fi
Run Code Online (Sandbox Code Playgroud)

-qgrep表达式中添加了一个标志,以便它不会打印匹配项; 它只会将匹配的存在视为"真实".

  • 错误的!**您的答案很快会为 `myarray=($'not\nmypattern')` 返回 `true`** 。以下修复了该问题,但需要最新版本的 `grep` (它会理解 `-z`),并且也可以正确地适用于您可能想到的任何 `$mypattern`: `printf '%s\0' "${myarray[@] }" | grep -Fqxz --“$mypattern”` (5认同)
  • @Tino 你认为你可以以礼貌的方式表达这种纠正,从而改善社区吗? (5认同)
  • 很好的解决方案!在 GNU grep 上,还有“--line-regexp”可以替换模式中的“-P”和 ^ 和 $: printf '%s\n' ${myarray[@]} | grep -q --line-regexp 'mypattern' (3认同)
  • @Fmstrat我想你会发现即使数组值有空格它也能在bash中工作。1. 您是否指定了带有引号的 `"${myarray[@]}"` ?2. 您是否使用 echo "${#myarray[@]}"` 来验证您的数组本身是否具有您期望的项目数量(并且不认为空格分隔项目)? (2认同)

Leo*_*ael 18

如果您需要性能,则不希望每次搜索时都遍历整个阵列.

在这种情况下,您可以创建表示该数组索引的关联数组(哈希表或字典).即它将每个数组元素映射到数组中的索引:

make_index () {
  local index_name=$1
  shift
  local -a value_array=("$@")
  local i
  # -A means associative array, -g means create a global variable:
  declare -g -A ${index_name}
  for i in "${!value_array[@]}"; do
    eval ${index_name}["${value_array[$i]}"]=$i
  done
}
Run Code Online (Sandbox Code Playgroud)

然后你可以像这样使用它:

myarray=('a a' 'b b' 'c c')
make_index myarray_index "${myarray[@]}"
Run Code Online (Sandbox Code Playgroud)

并测试会员资格如下:

member="b b"
# the "|| echo NOT FOUND" below is needed if you're using "set -e"
test "${myarray_index[$member]}" && echo FOUND || echo NOT FOUND
Run Code Online (Sandbox Code Playgroud)

或者:

if [ "${myarray_index[$member]}" ]; then 
  echo FOUND
fi
Run Code Online (Sandbox Code Playgroud)

请注意,即使测试值或数组值中有空格,此解决方案也能正确执行.

作为奖励,您还可以获得数组中值的索引:

echo "<< ${myarray_index[$member]} >> is the index of $member"
Run Code Online (Sandbox Code Playgroud)


小智 17

我通常只使用:

inarray=$(echo ${haystack[@]} | grep -o "needle" | wc -w)
Run Code Online (Sandbox Code Playgroud)

非零值表示找到了匹配项.

  • 确实,这绝对是最简单的解决方案 - 在我看来应该标记为答案。至少有我的赞成票![: (2认同)
  • 这不适用于类似的针头.例如,`haystack =(needle1 needle2); echo $ {haystack [@]} | grep -o"needle"| wc -w` (2认同)
  • 使用grep -x可以避免误报:`inarray = $(printf",%s""$ {haystack [@]}")| grep -x"needle"| wc -l` (2认同)

est*_*ani 13

另一个没有功能的衬垫:

(for e in "${array[@]}"; do [[ "$e" == "searched_item" ]] && exit 0; done) && echo "found" || echo "not found"
Run Code Online (Sandbox Code Playgroud)

感谢@Qwerty关于空间的提醒!

相应功能:

find_in_array() {
  local word=$1
  shift
  for e in "$@"; do [[ "$e" == "$word" ]] && return 0; done
}
Run Code Online (Sandbox Code Playgroud)

例:

some_words=( these are some words )
find_in_array word "${some_words[@]}" || echo "expected missing! since words != word"
Run Code Online (Sandbox Code Playgroud)

  • @codeforester 这是旧的......但正如它所写的,你需要它才能摆脱它,这就是 `exit 0` 的作用(如果找到,则尽快停止)。 (2认同)

hor*_*bzz 9

这是一个小小的贡献:

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

注意:这种方式不区分"两个单词"的情况,但问题中不需要这样做.


Yan*_*ann 9

containsElement () { for e in "${@:2}"; do [[ "$e" = "$1" ]] && return 0; done; return 1; }
Run Code Online (Sandbox Code Playgroud)

现在正确处理空数组.


小智 7

如何检查 Bash 数组是否包含值


误报匹配

array=(a1 b1 c1 d1 ee)

[[ ${array[*]} =~ 'a' ]] && echo 'yes' || echo 'no'
# output:
yes

[[ ${array[*]} =~ 'a1' ]] && echo 'yes' || echo 'no'
# output:
yes

[[ ${array[*]} =~ 'e' ]] && echo 'yes' || echo 'no'
# output:
yes

[[ ${array[*]} =~ 'ee' ]] && echo 'yes' || echo 'no'
# output:
yes
Run Code Online (Sandbox Code Playgroud)

完全符合

为了寻找完全匹配,您的正则表达式模式需要在值之前和之后添加额外的空间,例如 (^|[[:space:]])"VALUE"($|[[:space:]])

# Exact match

array=(aa1 bc1 ac1 ed1 aee)

if [[ ${array[*]} =~ (^|[[:space:]])"a"($|[[:space:]]) ]]; then
    echo "Yes";
else
    echo "No";
fi
# output:
No

if [[ ${array[*]} =~ (^|[[:space:]])"ac1"($|[[:space:]]) ]]; then
    echo "Yes";
else
    echo "No";
fi
# output:
Yes

find="ac1"
if [[ ${array[*]} =~ (^|[[:space:]])"$find"($|[[:space:]]) ]]; then
    echo "Yes";
else
    echo "No";
fi
# output:
Yes
Run Code Online (Sandbox Code Playgroud)

有关更多使用示例,示例来源在这里

  • -1 表示 `array=('not act1')` 完全失败。这种错误的实现甚至在过去造成了主要的 PITA:安全适配器获取目录中所有文件的列表,并返回是否允许访问该目录。具有“dir_protect”的目录受到保护。由于目录中的上传总是附加扩展名,所以您很安全,对吗?黑客上传“dir_protect hack.txt”(其中包含空格,因为空格在 Windows 中很常见),从而创建 DoS(安全适配器无法直接访问该目录,只能访问传输的文件列表)。 (3认同)

Pau*_*ce. 6

如果你想做一个快速而肮脏的测试,看看是否值得迭代整个数组以获得精确匹配,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
Run Code Online (Sandbox Code Playgroud)

这将输出"正在检查"和"匹配".用array=(word "two words" something)它只输出"检查".随着array=(word "two widgets" something)就不会有输出.


Max*_*xim 6

以下是几种可能实现的汇编,包括集成验证和简单的基准测试(需要 Bash >= 4.0):

#!/usr/bin/env bash

# Check if array contains item [$1: item, $2: array name]
function in_array_1() {
    local needle="$1" item
    local -n arrref="$2"
    for item in "${arrref[@]}"; do
        [[ "${item}" == "${needle}" ]] && return 0
    done
    return 1
}

# Check if array contains item [$1: item, $2: array name]
function in_array_2() {
    local needle="$1" arrref="$2[@]" item
    for item in "${!arrref}"; do
        [[ "${item}" == "${needle}" ]] && return 0
    done
    return 1
}

# Check if array contains item [$1: item, $2: array name]
function in_array_3() {
    local needle="$1" i
    local -n arrref="$2"
    for ((i=0; i < ${#arrref[@]}; i++)); do
        [[ "${arrref[i]}" == "${needle}" ]] && return 0
    done
    return 1
}

# Check if array contains item [$1: item, $2..$n: array items]
function in_array_4() {
    local needle="$1" item
    shift
    for item; do
        [[ "${item}" == "${needle}" ]] && return 0
    done
    return 1
}

# Check if array contains item [$1: item, $2..$n: array items]
function in_array_5() {
    local needle="$1" item
    for item in "${@:2}"; do
        [[ "${item}" == "${needle}" ]] && return 0
    done
    return 1
}

# Check if array contains item [$1: item, $2: array name]
function in_array_6() {
    local needle="$1" arrref="$2[@]" array i
    array=("${!arrref}")
    for ((i=0; i < ${#array[@]}; i++)); do
        [[ "${array[i]}" == "${needle}" ]] && return 0
    done
    return 1
}

# Check if array contains item [$1: item, $2..$n: array items]
function in_array_7() {
    local needle="$1" array=("${@:2}") item
    for item in "${array[@]}"; do
        [[ "${item}" == "${needle}" ]] && return 0
    done
    return 1
}

# Check if array contains item [$1: item, $2..$n: array items]
function in_array_8() {
    local needle="$1"
    shift
    while (( $# > 0 )); do
        [[ "$1" == "${needle}" ]] && return 0
        shift
    done
    return 1
}


#------------------------------------------------------------------------------


# Generate map for array [$1: name of source array, $2: name of target array]
# NOTE: target array must be pre-declared by caller using 'declare -A <name>'
function generate_array_map() {
    local -n srcarr="$1" dstmap="$2"
    local i key
    dstmap=()
    for i in "${!srcarr[@]}"; do
        key="${srcarr[i]}"
        [[ -z ${dstmap["${key}"]+set} ]] && dstmap["${key}"]=${i} || dstmap["${key}"]+=,${i}
    done
}

# Check if array contains item [$1: item, $2: name of array map]
function in_array_9() {
    local needle="$1"
    local -n mapref="$2"
    [[ -n "${mapref["${needle}"]+set}" ]] && return 0 || return 1
}


#------------------------------------------------------------------------------


# Test in_array function [$1: function name, $2: function description, $3: test array size]
function test() {
    local tname="$1" tdesc="$2" tn=$3 ti=0 tj=0 ta=() tct=0 tepapre="" tepapost="" tepadiff=()
    local -A tam=()

    echo -e "\e[1m${tname} (${tdesc}):\e[0m"

    # Generate list of currently defined variables
    tepapre="$(compgen -v)"

    # Fill array with random items
    for ((ti=0; ti < ${tn}; ti++)); do
        ta+=("${RANDOM} ${RANDOM} ${RANDOM} ${RANDOM}")
    done

    # Determine function call type (pass array items, pass array name, pass array map)
    case "${tname}" in
        "in_array_1"|"in_array_2"|"in_array_3"|"in_array_6") tct=0; ;;
        "in_array_4"|"in_array_5"|"in_array_7"|"in_array_8") tct=1; ;;
        "in_array_9") generate_array_map ta tam; tct=2; ;;
        *) echo "Unknown in_array function '${tname}', aborting"; return 1; ;;
    esac

    # Verify in_array function is working as expected by picking a few random
    # items and checking
    echo -e "\e[1mVerification...\e[0m"
    for ((ti=0; ti < 10; ti++)); do
        tj=$(( ${RANDOM} % ${#ta[@]} ))
        echo -n "Item ${tj} '${ta[tj]}': "
        if (( ${tct} == 0 )); then
            "${tname}" "${ta[tj]}" ta && echo -en "\e[1;32mok\e[0m" || echo -en "\e[1;31mnok\e[0m"
            echo -n " "
            "${tname}" "${ta[tj]}.x" ta && echo -en "\e[1;31mnok\e[0m" || echo -en "\e[1;32mok\e[0m"
        elif (( ${tct} == 1 )); then
            "${tname}" "${ta[tj]}" "${ta[@]}" && echo -en "\e[1;32mok\e[0m" || echo -en "\e[1;31mnok\e[0m"
            echo -n " "
            "${tname}" "${ta[tj]}.x" "${ta[@]}" && echo -en "\e[1;31mnok\e[0m" || echo -en "\e[1;32mok\e[0m"
        elif (( ${tct} == 2 )); then
            "${tname}" "${ta[tj]}" tam && echo -en "\e[1;32mok\e[0m" || echo -en "\e[1;31mnok\e[0m"
            echo -n " "
            "${tname}" "${ta[tj]}.x" tam && echo -en "\e[1;31mnok\e[0m" || echo -en "\e[1;32mok\e[0m"
        fi
        echo
    done

    # Benchmark in_array function
    echo -en "\e[1mBenchmark...\e[0m"
    time for ((ti=0; ti < ${#ta[@]}; ti++)); do
        if (( ${tct} == 0 )); then
            "${tname}" "${ta[ti]}" ta
        elif (( ${tct} == 1 )); then
            "${tname}" "${ta[ti]}" "${ta[@]}"
        elif (( ${tct} == 2 )); then
            "${tname}" "${ta[ti]}" tam
        fi
    done

    # Generate list of currently defined variables, compare to previously
    # generated list to determine possible environment pollution
    echo -e "\e[1mEPA test...\e[0m"
    tepapost="$(compgen -v)"
    readarray -t tepadiff < <(echo -e "${tepapre}\n${tepapost}" | sort | uniq -u)
    if (( ${#tepadiff[@]} == 0 )); then
        echo -e "\e[1;32mclean\e[0m"
    else
        echo -e "\e[1;31mpolluted:\e[0m ${tepadiff[@]}"
    fi

    echo
}


#------------------------------------------------------------------------------


# Test in_array functions
n=5000
echo
( test in_array_1 "pass array name, nameref reference, for-each-loop over array items" ${n} )
( test in_array_2 "pass array name, indirect reference, for-each-loop over array items" ${n} )
( test in_array_3 "pass array name, nameref reference, c-style for-loop over array items by index" ${n} )
( test in_array_4 "pass array items, for-each-loop over arguments" ${n} )
( test in_array_5 "pass array items, for-each-loop over arguments as array" ${n} )
( test in_array_6 "pass array name, indirect reference + array copy, c-style for-loop over array items by index" ${n} )
( test in_array_7 "pass array items, copy array from arguments as array, for-each-loop over array items" ${n} )
( test in_array_8 "pass array items, while-loop, shift over arguments" ${n} )
( test in_array_9 "pre-generated array map, pass array map name, direct test without loop" ${n} )
Run Code Online (Sandbox Code Playgroud)

结果:

in_array_1 (pass array name, nameref reference, for-each-loop over array items):
Verification...
Item 862 '19528 10140 12669 17820': ok ok
Item 2250 '27262 30442 9295 24867': ok ok
Item 4794 '3857 17404 31925 27993': ok ok
Item 2532 '14553 12282 26511 32657': ok ok
Item 1911 '21715 8066 15277 27126': ok ok
Item 4289 '3081 10265 16686 19121': ok ok
Item 4837 '32220 1758 304 7871': ok ok
Item 901 '20652 23880 20634 14286': ok ok
Item 2488 '14578 8625 30251 9343': ok ok
Item 4165 '4514 25064 29301 7400': ok ok
Benchmark...
real    1m11,796s
user    1m11,262s
sys     0m0,473s
EPA test...
clean

in_array_2 (pass array name, indirect reference, for-each-loop over array items):
Verification...
Item 2933 '17482 25789 27710 2096': ok ok
Item 3584 '876 14586 20885 8567': ok ok
Item 872 '176 19749 27265 18038': ok ok
Item 595 '6597 31710 13266 8813': ok ok
Item 748 '569 9200 28914 11297': ok ok
Item 3791 '26477 13218 30172 31532': ok ok
Item 2900 '3059 8457 4879 16634': ok ok
Item 676 '23511 686 589 7265': ok ok
Item 2248 '31351 7961 17946 24782': ok ok
Item 511 '8484 23162 11050 426': ok ok
Benchmark...
real    1m11,524s
user    1m11,086s
sys     0m0,437s
EPA test...
clean

in_array_3 (pass array name, nameref reference, c-style for-loop over array items by index):
Verification...
Item 1589 '747 10250 20133 29230': ok ok
Item 488 '12827 18892 31996 1977': ok ok
Item 801 '19439 25243 24485 24435': ok ok
Item 2588 '17193 18893 21610 9302': ok ok
Item 4436 '7100 655 8847 3068': ok ok
Item 2620 '19444 6457 28835 24717': ok ok
Item 4398 '4420 16336 612 4255': ok ok
Item 2430 '32397 2402 12631 29774': ok ok
Item 3419 '906 5361 32752 7698': ok ok
Item 356 '9776 16485 20838 13330': ok ok
Benchmark...
real    1m17,037s
user    1m17,019s
sys     0m0,005s
EPA test...
clean

in_array_4 (pass array items, for-each-loop over arguments):
Verification...
Item 1388 '7932 15114 4025 15625': ok ok
Item 3900 '23863 25328 5632 2752': ok ok
Item 2678 '31296 4216 17485 8874': ok ok
Item 1893 '16952 29047 29104 23384': ok ok
Item 1616 '19543 5999 4485 22929': ok ok
Item 93 '14456 2806 12829 19552': ok ok
Item 265 '30961 19733 11863 3101': ok ok
Item 4615 '10431 9566 25767 13518': ok ok
Item 576 '11726 15104 11116 74': ok ok
Item 3829 '19371 25026 6252 29478': ok ok
Benchmark...
real    1m30,912s
user    1m30,740s
sys     0m0,011s
EPA test...
clean

in_array_5 (pass array items, for-each-loop over arguments as array):
Verification...
Item 1012 '29213 31971 21483 30225': ok ok
Item 2802 '4079 5423 29240 29619': ok ok
Item 473 '6968 798 23936 6852': ok ok
Item 2183 '20734 4521 30800 2126': ok ok
Item 3059 '14952 9918 15695 19309': ok ok
Item 1424 '25784 28380 14555 21893': ok ok
Item 1087 '16345 19823 26210 20083': ok ok
Item 257 '28890 5198 7251 3866': ok ok
Item 3986 '29035 19288 12107 3857': ok ok
Item 2509 '9219 32484 12842 27472': ok ok
Benchmark...
real    1m53,485s
user    1m53,404s
sys     0m0,077s
EPA test...
clean

in_array_6 (pass array name, indirect reference + array copy, c-style for-loop over array items by index):
Verification...
Item 4691 '25498 10521 20673 14948': ok ok
Item 263 '25265 29824 3876 14088': ok ok
Item 2550 '2416 14274 12594 29740': ok ok
Item 2269 '2769 11436 3622 28273': ok ok
Item 3246 '23730 25956 3514 17626': ok ok
Item 1059 '10776 12514 27222 15640': ok ok
Item 53 '23813 13365 16022 4092': ok ok
Item 1503 '6593 23540 10256 17818': ok ok
Item 2452 '12600 27404 30960 26759': ok ok
Item 2526 '21190 32512 23651 7865': ok ok
Benchmark...
real    1m54,793s
user    1m54,326s
sys     0m0,457s
EPA test...
clean

in_array_7 (pass array items, copy array from arguments as array, for-each-loop over array items):
Verification...
Item 2212 '12127 12828 27570 7051': ok ok
Item 1393 '19552 26263 1067 23332': ok ok
Item 506 '18818 8253 14924 30710': ok ok
Item 789 '9803 1886 17584 32686': ok ok
Item 1795 '19788 27842 28044 3436': ok ok
Item 376 '4372 16953 17280 4031': ok ok
Item 4846 '19130 6261 21959 6869': ok ok
Item 2064 '2357 32221 22682 5814': ok ok
Item 4866 '10928 10632 19175 14984': ok ok
Item 1294 '8499 11885 5900 6765': ok ok
Benchmark...
real    2m35,012s
user    2m33,578s
sys     0m1,433s
EPA test...
clean

in_array_8 (pass array items, while-loop, shift over arguments):
Verification...
Item 134 '1418 24798 20169 9501': ok ok
Item 3986 '12160 12021 29794 29236': ok ok
Item 1607 '26633 14260 18227 898': ok ok
Item 2688 '18387 6285 2385 18432': ok ok
Item 603 '1421 306 6102 28735': ok ok
Item 625 '4530 19718 30900 1938': ok ok
Item 4033 '9968 24093 25080 8179': ok ok
Item 310 '6867 9884 31231 29173': ok ok
Item 661 '3794 4745 26066 22691': ok ok
Item 4129 '3039 31766 6714 4921': ok ok
Benchmark...
real    5m51,097s
user    5m50,566s
sys     0m0,495s
EPA test...
clean

in_array_9 (pre-generated array map, pass array map name, direct test without loop):
Verification...
Item 3696 '661 6048 13881 26901': ok ok
Item 815 '29729 13733 3935 20697': ok ok
Item 1076 '9220 3405 18448 7240': ok ok
Item 595 '8912 2886 13678 24066': ok ok
Item 2803 '13534 23891 5344 652': ok ok
Item 1810 '12528 32150 7050 1254': ok ok
Item 4055 '21840 7436 1350 15443': ok ok
Item 2416 '19550 28434 17110 31203': ok ok
Item 1630 '21054 2819 7527 953': ok ok
Item 1044 '30152 22211 22226 6950': ok ok
Benchmark...
real    0m0,128s
user    0m0,128s
sys     0m0,000s
EPA test...
clean
Run Code Online (Sandbox Code Playgroud)


Ant*_*dge 6

停止疯狂吧!让您的解决方案简单、干净且可重复使用。

这些函数考虑索引数组和关联数组。可以通过将搜索算法从线性搜索升级为二进制搜索(对于大型数据集)来改进它们。

##
# Determines if a value exists in an array.
###
function hasArrayValue ()
{
    local -r needle="{$1:?}"
    local -nr haystack="{$2:?}"  # Where you pass by reference to get the entire array in one argument.

    # Linear search. Upgrade to binary search for large datasets.
    for value in "${haystack[@]}"; do
        if [[ "$value" == "$needle" ]]; then
            return 0
        fi
    done

    return 1
}

##
# Determines if a value exists in an associative array / map.
###
function hasMapValue ()
{
    local -r needle="{$1:?}"
    local -nr haystack="{$2:?}"

    # Linear search. Upgrade to binary search for large datasets.
    for value in "${haystack[@]}"; do
        if [[ $value == $needle ]]; then
            return 0
        fi
    done

    return 1
}
Run Code Online (Sandbox Code Playgroud)

是的,相同的逻辑,但在处理 bash 时,如果有一个函数的名称可以让您知道正在迭代(或不迭代)的内容,可能会很有用。

  • @geekyj `-r` 是只读的。`-n` 使您可以传入名称引用。因此,当使用“-n”作为 bash 函数的输入参数时,在客户端代码(调用该函数的代码)中,不要使用 $var 形式的变量或数组。**仅传递名称。** 示例:`hasMapValue $targetValue myBashMapArray` (2认同)

小智 5

a=(b c d)

if printf '%s\0' "${a[@]}" | grep -Fqxz c
then
  echo 'array “a” contains value “c”'
fi
Run Code Online (Sandbox Code Playgroud)

如果您愿意,可以使用等效的长选项:

--fixed-strings --quiet --line-regexp --null-data
Run Code Online (Sandbox Code Playgroud)


Chr*_*nce 5

这为我工作:

# traditional system call return values-- used in an `if`, this will be true when returning 0. Very Odd.
contains () {
    # odd syntax here for passing array parameters: http://stackoverflow.com/questions/8082947/how-to-pass-an-array-to-a-bash-function
    local list=$1[@]
    local elem=$2

    # echo "list" ${!list}
    # echo "elem" $elem

    for i in "${!list}"
    do
        # echo "Checking to see if" "$i" "is the same as" "${elem}"
        if [ "$i" == "${elem}" ] ; then
            # echo "$i" "was the same as" "${elem}"
            return 0
        fi
    done

    # echo "Could not find element"
    return 1
}
Run Code Online (Sandbox Code Playgroud)

示例调用:

arr=("abc" "xyz" "123")
if contains arr "abcx"; then
    echo "Yes"
else
    echo "No"
fi
Run Code Online (Sandbox Code Playgroud)


Dej*_*ton 5

借用Dennis Williamson回答,以下解决方案结合了数组、shell 安全引用和正则表达式,以避免需要:迭代循环;使用管道或其他子流程;或使用非 bash 实用程序。

declare -a array=('hello, stack' one 'two words' words last)
printf -v array_str -- ',,%q' "${array[@]}"

if [[ "${array_str},," =~ ,,words,, ]]
then
   echo 'Matches'
else
   echo "Doesn't match"
fi
Run Code Online (Sandbox Code Playgroud)

上面的代码通过使用 Bash 正则表达式来匹配数组内容的字符串化版本。有六个重要步骤可以确保正则表达式匹配不会被数组中值的巧妙组合所欺骗:

  1. 使用 Bash 的内置printfshell 引用构造比较字符串%q。Shell-quoting 将通过使用反斜杠进行转义来确保特殊字符成为“shell-safe” \
  2. 选择一个特殊字符作为值分隔符。分隔符必须是使用时将被转义的特殊字符之一%q;这是保证数组中的值不能以巧妙的方式构造来欺骗正则表达式匹配的唯一方法。我选择逗号,是因为当以其他意想不到的方式进行评估或误用时,该字符是最安全的。
  3. 将所有数组元素组合成一个字符串,使用特殊字符的两个实例作为分隔符。以逗号为例,我将其,,%q用作printf. 这很重要,因为特殊字符的两个实例只能在作为分隔符出现时彼此相邻出现;特殊字符的所有其他实例都将被转义。
  4. 将分隔符的两个尾随实例附加到字符串,以允许匹配数组的最后一个元素。因此,与其进行比较${array_str},不如与进行比较${array_str},,
  5. 如果您搜索的目标字符串是由用户变量提供的,您必须使用反斜杠转义特殊字符的所有实例。否则,正则表达式匹配很容易被巧妙设计的数组元素所欺骗。
  6. 对字符串执行 Bash 正则表达式匹配。


kva*_*our 5

大多数投票的答案非常简洁明了,但是当空格是数组元素之一的一部分时,它可能会出现误报。更改 IFS 并使用"${array[*]}"代替"${array[@]}". 方法是相同的,但看起来不太干净。通过使用"${array[*]}",我们打印 的所有元素$array,由 中的第一个字符分隔IFS。因此,通过选择正确的IFS,您可以克服这个特殊问题。在这种特殊情况下,我们决定设置IFS一个不常见的字符$'\001',它代表标题开头( SOH)

$ array=("foo bar" "baz" "qux")
$ IFS=$'\001'
$ [[ "$IFS${array[*]}$IFS" =~ "${IFS}foo${IFS}" ]] && echo yes || echo no
no
$ [[ "$IFS${array[*]}$IFS" =~ "${IFS}foo bar${IFS}" ]] && echo yes || echo no
yes
$ unset IFS
Run Code Online (Sandbox Code Playgroud)

这解决了大多数误报问题,但需要很好地选择IFS.

注意:如果IFS之前设置过,最好保存并重置,而不是使用unset IFS


有关的: