检查Bash数组是否包含值


441

在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

我不确定这是否是最好的解决方案,但似乎可行。

Answers:


456

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

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

if [[ ! " ${array[@]} " =~ " ${value} " ]]; then
    # whatever you want to do when array doesn't contain value
fi

请注意,如果要搜索的值是带有空格的数组元素中的单词之一,它将给出假肯定。例如

array=("Jack Brown")
value="Jack"

正则表达式会看到“ Jack”在数组中,即使它不在。因此,IFS如果您仍想使用此解决方案,则必须更改正则表达式上的分隔符和

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

if [[ "\t${array[@]}\t" =~ "\t${value}\t" ]]; then
    echo "true"
else
    echo "false"
fi

这将打印“ false”。

显然,这也可以用作测试语句,允许将其表示为单线

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

1
我在第一个正则表达式值匹配的开头添加了一个空格,以便它仅与单词匹配,而不与单词结尾匹配。效果很好。但是,我不明白您为什么要使用第二个条件,第一个条件不能单独使用吗?
JStrahl

1
@AwQiruiGuo我不确定我正在关注。您在谈论带有美元文字的数组吗?如果是这样,只需确保将与之匹配的值中的美元转义为反斜杠即可。
Keegan 2015年

10
Oneliner: [[ " ${branches[@]} " =~ " ${value} " ]] && echo "YES" || echo "NO";
ericson.cepeda

3
Shellcheck抱怨此解决方案SC2199和SC2076。如果不破坏功能,我将无法修复警告。除了为该行禁用shellcheck以外,还有其他想法吗?
阿里·埃萨姆

4
SC2076易于修复,只需删除中的双引号即可if。我认为没有办法避免这种方式的SC2199。如其他解决方案所示,您必须显式地遍历数组,或者忽略警告。
基冈

388

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

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

该功能的测试运行可能类似于:

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

5
很好用!我只需要记住像引号一样传递数组 "${array[@]}"。否则,包含空格的元素将破坏功能。
2012年

26
真好 我称它为elementIn(),因为它检查第二个参数是否包含第一个参数。containsElements()听起来像数组会先出现。对于像我这样的新手,如何使用一个不使用“ if”语句中的标准输出的函数的示例将有所帮助:if elementIn "$table" "${skip_tables[@]}" ; then echo skipping table: ${table}; fi; 谢谢您的帮助!
GlenPeterson

5
@Bluz &&构造是一个布尔AND运算符。布尔运算符的使用会创建一个布尔语句。布尔逻辑表示,只有在&&之前和之后的两个语句都为true时,整个语句才能为true。这用作in和if块的快捷方式。对测试进行评估,如果为false,则无需评估返回值,因为一旦测试失败并因此无法运行,则返回值与整个语句无关。如果测试成功,则布尔语句的成功确实需要确定返回的结果,以便运行代码。
peteches

4
@James按照惯例,bash中的成功代码为“ 0”,并且错误均为> =1。这就是为什么成功时返回0的原因。:)
tftd

11
@Stelios shift将参数列表向左移动1(删除第一个参数),for而不会in隐式迭代参数列表。
基督教

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

69
请注意,这不会分别遍历数组中的每个元素……相反,它只是连接数组并匹配“ two”作为子字符串。如果要测试确切的单词“二”是否是数组中的一个元素,则可能会导致不良行为。
MartyMacGyver,2013年

我以为这在比较文件类型时对我有用,但是发现随着计数器的增加,它计数的值太多了……嘘!
Mike Q

17
错误!原因:case "${myarray[@]}" in *"t"*) echo "found" ;; esac输出:found
Sergej Jevsejev'8

@MartyMacGyver,能否请您看看我对这个答案的补充内容stackoverflow.com/a/52414872/1619950
Aleksandr Podkutin

45
for i in "${array[@]}"
do
    if [ "$i" -eq "$yourValue" ] ; then
        echo "Found"
    fi
done

对于字符串:

for i in "${array[@]}"
do
    if [ "$i" == "$yourValue" ] ; then
        echo "Found"
    fi
done

也就是说,您可以使用带索引的for循环,并避免在数组元素包含IFS时被杀死:for((i = 0; i <$ {
#array

@Matt:${#}由于Bash支持稀疏数组,因此必须谨慎使用。
暂停,直到另行通知。

@Paolo,如果您的数组包含空格,则将其作为字符串进行比较。一个空格也是一个字符串。
Scott

@Paolo:您可以将该函数设为函数,但是不能将数组作为参数传递,因此必须将其视为全局变量。
暂停,直到另行通知。

丹尼斯是对的。在bash参考手册中:“如果单词被双引号,... $ {name [@]}会将name的每个元素扩展为单独的单词”
mkb 2010年

37

一线解决方案

printf '%s\n' ${myarray[@]} | grep -P '^mypattern$'

说明

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

grep语句使用特殊字符^$找到一行,该行恰好包含指定的模式mypattern(不多也不少)。


用法

将此if ... then声明为:

if printf '%s\n' ${myarray[@]} | grep -q -P '^mypattern$'; then
    # ...
fi

-qgrep表达式中添加了一个标志,这样它就不会打印匹配项。它将匹配的存在视为“ true”。


不错的解决方案!在GNU grep上,还有“ --line-regexp”,它可以替换模式中的“ -P”以及^和$:printf'%s \ n'$ {myarray [@]} | grep -q --line-regexp'mypattern'–
presto8

19

如果您需要性能,则不想每次搜索时都遍历整个数组。

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

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
}

然后,您可以像这样使用它:

myarray=('a a' 'b b' 'c c')
make_index myarray_index "${myarray[@]}"

并像这样测试成员资格:

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

或者:

if [ "${myarray_index[$member]}" ]; then 
  echo FOUND
fi

请注意,即使测试值或数组值中有空格,此解决方案也可以执行正确的操作。

另外,您还可以通过以下方式获取数组中值的索引:

echo "<< ${myarray_index[$member]} >> is the index of $member"

+1表示应该使用关联数组。我认为make_index由于间接的原因,其代码有些虚构;您可以使用固定的数组名称和简单得多的代码。
musiphil

17

我通常只使用:

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

非零值表示找到匹配项。


没错,这绝对是最简单的解决方案-在我看来应该标记为答案。至少有我的支持![:
ToVine

2
这不适用于类似的针头。例如,haystack=(needle1 needle2); echo ${haystack[@]} | grep -o "needle" | wc -w
Keegan

1
非常真实 加入没有任何元素的定界符并将其添加到针中将对此有所帮助。也许像...(未经测试)inarray=$(printf ",%s" "${haystack[@]}") | grep -o ",needle" | wc -w)
肖恩·迪桑蒂

2
使用grep -x可以避免误报: inarray=$(printf ",%s" "${haystack[@]}") | grep -x "needle" | wc -l
jesjimher

也许只是inarray=$(echo " ${haystack[@]}" | grep -o " needle" | wc -w)-x导致grep尝试匹配整个输入字符串
MI Wright,

17

另一个没有功能的班轮:

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

感谢@Qwerty有关空间的注意事项!

对应功能:

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

例:

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

1
为什么我们在这里需要一个子shell?
codeforester

1
@codeforester这是旧的...但是正如写的那样,您需要它才能从中摆脱出来,这就是它的exit 0作用(如果找到,则立即停止)。
estani

所述一个衬套的端部应是|| echo not found代替|| not found或外壳将尝试的名称来执行命令带有参数发现如果所请求的值不是在数组中。
开玩笑

11
containsElement () { for e in "${@:2}"; do [[ "$e" = "$1" ]] && return 0; done; return 1; }

现在可以正确处理空数组。


这与@patrik的答案有何不同?我看到的唯一区别是"$e" = "$1"(而不是"$e" == "$1")它看起来像个错误。
CivFan 2015年

1
它不是。那时,@ patrik将我的评论合并到他的原始答案中(补丁4)。注意:"e" == "$1"在语法上更清晰。
Yann 2015年

@CivFan以目前的形式,它比patrik的回答要短,因为优雅的$ {@:2}和自我记录的$ 1。我要补充一点,在[[]]中不需要引用。
洪特瓦里·莱文特

9

这是一个小贡献:

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

注意:这种方式不能区分大小写“两个单词”,但这在问题中不是必需的。


这对我很有帮助。谢谢!
Ed Manet

该问题并未明确指出您必须给出正确的答案,但我认为这是隐含在问题中的……该数组不包含值“ two”。
tetsujin

上面将报告“ rd”的匹配项。
Noel Yap

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

这将输出“检查中”和“匹配”。使用array=(word "two words" something)它只会输出“检查中”。随着array=(word "two widgets" something)将没有输出。


为什么不只用只匹配整个字符串words的正则表达式替换呢^words$,这完全消除了单独检查每个项目的需要?
Dejay Clayton

@DejayClayton:因为pattern='^words$'; if [[ ${array[@]} =~ $pattern ]]永远不会匹配,因为它会像检查标量一样立即检查整个数组。仅在有基于粗略匹配的理由继续进行时,才对我的答案进行个别检查。
暂停,直到另行通知。

啊,我明白了你要做什么。我提出了一个变种的答案,该答案更加有效和安全。
Dejay Clayton

6

这为我工作:

# 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
}

示例调用:

arr=("abc" "xyz" "123")
if contains arr "abcx"; then
    echo "Yes"
else
    echo "No"
fi

5
a=(b c d)

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

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

--fixed-strings --quiet --line-regexp --null-data

1
这在Mac上的BSD-grep上不起作用,因为没有--null-data。:(
威尔

4

借用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

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

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

非常聪明。我可以看到大多数潜在问题都可以避免,但是我想测试一下是否有任何极端情况。另外,我想看一个处理第5点的例子printf -v pattern ',,%q,,' "$user_input"; if [[ "${array_str},," =~ $pattern ]]
暂停,直到另行通知。

case "$(printf ,,%q "${haystack[@]}"),," in (*"$(printf ,,%q,, "$needle")"*) true;; (*) false;; esac
蒂诺

3

@ ghostdog74关于使用case逻辑检查数组是否包含特定值的答案的一个小补充:

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

或者extglob打开选项,您可以这样操作:

myarray=(one two three)
word=two
shopt -s extglob
case "${myarray[@]}" in ?(*" ")"$word"?(" "*)) echo "found" ;; esac

我们也可以使用if语句来做到这一点:

myarray=(one two three)
word=two
if [[ $(printf "_[%s]_" "${myarray[@]}") =~ .*_\[$word\]_.* ]]; then echo "found"; fi

2

给出:

array=("something to search for" "a string" "test2000")
elem="a string"

然后简单检查一下:

if c=$'\x1E' && p="${c}${elem} ${c}" && [[ ! "${array[@]/#/${c}} ${c}" =~ $p ]]; then
  echo "$elem exists in array"
fi

哪里

c is element separator
p is regex pattern

(之所以单独分配p而不是直接在[[]]内部使用表达式是为了保持对bash 4的兼容性)


喜欢在这里使用“简单”一词...
克里斯蒂安(Christian)

2

结合此处介绍的一些思想,您可以使无言的陈述完全匹配单词,从而达到优雅的效果。

$find="myword"
$array=(value1 value2 myword)
if [[ ! -z $(printf '%s\n' "${array[@]}" | grep -w $find) ]]; then
  echo "Array contains myword";
fi

这不会在word或上触发val,只会匹配整个单词。如果每个数组值包含多个单词,它将中断。


1

我通常编写这类实用程序来对变量的名称进行操作,而不是对变量值进行操作,这主要是因为bash无法通过引用传递变量。

这是使用数组名称的版本:

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
}

这样,问题示例变为:

array_contains A "one" && echo "contains one"

等等


有人可以张贴在if中使用的示例,尤其是如何在数组中传递。我试图检查是否通过将参数作为数组传递给脚本的参数,但是它不起作用。如果[[$ {check} == 1]]; params =(“ $ @”)check = array_contains $ {params}'SKIPDIRCHECK'; 然后....但是,当使用'asas'作为参数运行脚本时,它总是说asas:命令未找到。:/
史蒂夫·柴尔德斯

1

使用grepprintf

格式化每个数组成员的新行,然后再grep行。

if printf '%s\n' "${array[@]}" | grep -x -q "search string"; then echo true; else echo false; fi
例:
$ array=("word", "two words")
$ if printf '%s\n' "${array[@]}" | grep -x -q "two words"; then echo true; else echo false; fi
true

请注意,这对于分度符和空格没有问题。


1

单行检查,无“ grep”和循环

if ( dlm=$'\x1F' ; IFS="$dlm" ; [[ "$dlm${array[*]}$dlm" == *"$dlm${item}$dlm"* ]] ) ; then
  echo "array contains '$item'"
else
  echo "array does not contain '$item'"
fi

这种方法既不使用外部实用程序,grep也不使用循环。

这里发生的是:

  • 我们使用通配符子字符串匹配器在连接成字符串的数组中找到我们的项目;
  • 通过将搜索项放在一对定界符之间,我们消除了可能的误报;
  • 为了安全起见,我们使用不可打印的字符作为分隔符;
  • 通过临时替换IFS变量值,我们也实现了将定界符用于数组连接;
  • 我们IFS通过在子外壳中(一对括号内)评估条件表达式来使此值替换为临时操作

消除dlm。直接使用IFS。
罗宾·米德

这是最好的答案。我非常喜欢它,我使用这种技术编写了一个函数
罗宾·米德

1

使用参数扩展:

$ {parameter:+ word}如果参数为null或未设置,则不替换任何内容,否则替换word的扩展名。

declare -A myarray
myarray[hello]="world"

for i in hello goodbye 123
do
  if [ ${myarray[$i]:+_} ]
  then
    echo ${!myarray[$i]} ${myarray[$i]} 
  else
    printf "there is no %s\n" $i
  fi
done

${myarray[hello]:+_}适用于关联数组,但不适用于通常的索引数组。问题是关于在数组中查找值,而不是检查关联数组的键是否存在。
埃里克

0

回答完之后,我读了一个我特别喜欢的答案,但是它有缺陷并且被否决了。我受到启发,这是我认为可行的两种新方法。

array=("word" "two words") # let's look for "two words"

使用grepprintf

(printf '%s\n' "${array[@]}" | grep -x -q "two words") && <run_your_if_found_command_here>

使用for

(for e in "${array[@]}"; do [[ "$e" == "two words" ]] && exit 0; done; exit 1) && <run_your_if_found_command_here>

对于not_found结果,请添加 || <run_your_if_notfound_command_here>


0

这是我的看法。

如果可以避免,我宁愿不使用bash for循环,因为这会花费一些时间。如果某些东西必须循环,那么就应该使用比Shell脚本低级的语言编写的东西。

function array_contains { # arrayname value
  local -A _arr=()
  local IFS=
  eval _arr=( $(eval printf '[%q]="1"\ ' "\${$1[@]}") )
  return $(( 1 - 0${_arr[$2]} ))
}

这是通过创建一个临时的关联数组来工作的,_arr该数组的索引是从输入数组的值派生的。(请注意,关联数组在bash 4及更高版本中可用,因此该功能在bash的早期版本中不起作用。)我们设置$IFS为避免在空白处进行单词拆分。

该函数不包含任何显式循环,尽管内部bash会逐步遍历输入数组以填充printf。printf格式用于%q确保转义输入数据,以便可以安全地将它们用作数组键。

$ a=("one two" three four)
$ array_contains a three && echo BOOYA
BOOYA
$ array_contains a two && echo FAIL
$

请注意,此函数使用的所有内容都是bash的内置功能,因此,即使在命令扩展中,也没有外部管道将您拖到下面。

而且,如果您不喜欢使用eval...,那么您可以自由使用另一种方法。:-)


如果数组包含方括号怎么办?
gniourf_gniourf

@gniourf_gniourf-如果方括号是平衡的,那似乎很好,但是如果您的数组包含带有不平衡方括号的值,我会发现这是一个问题。在这种情况下,我将eval在答案的末尾调用该指令。:)
ghoti

不是我不喜欢eval(我没有反对它的权利,不像大多数哭泣的人eval是邪恶的,而大多数人却不了解这是什么邪恶)。只是您的命令已损坏。也许%q而不是%s会更好。
gniourf_gniourf

1
@gniourf_gniourf:我只是说“另一种方法”(eval显然,我完全支持你),但是你是绝对正确的,%q似乎对你有所帮助,而没有破坏我所能看到的任何其他东西。(我没有意识到%q也将转义为方括号。)我看到并解决的另一个问题是关于空白。使用a=(one "two " three),类似于Keegan的问题:不仅array_contains a "two "得到了假阴性,而且array_contains a two得到了假阳性。通过设置很容易修复IFS
ghoti

关于空格,不是因为缺少引号吗?它也以glob字符中断。我想您想要的是: eval _arr=( $(eval printf '[%q]="1"\ ' "\"\${$1[@]}\"") ),您可以抛弃它local IFS=。数组中的空字段仍然存在问题,因为Bash会拒绝在关联数组中创建空键。解决该问题的一种快速方法是在虚拟字符前加上xeval _arr=( $(eval printf '[x%q]="1"\ ' "\"\${$1[@]}\"") )return $(( 1 - 0${_arr[x$2]} ))
gniourf_gniourf

-1

我建议使用的我的正则表达式技术版本:

values=(foo bar)
requestedValue=bar

requestedValue=${requestedValue##[[:space:]]}
requestedValue=${requestedValue%%[[:space:]]}
[[ "${values[@]/#/X-}" =~ "X-${requestedValue}" ]] || echo "Unsupported value"

这里发生的事情是,您正在将整个支持的值数组扩展为单词,并在每个单词的前面添加一个特定的字符串“ X-”,并对请求的值执行相同的操作。如果这个确实包含在数组中,那么结果字符串最多将与结果标记之一匹配,或者完全不匹配。在后一种情况下|| 运算符触发,您知道您正在处理一个不受支持的值。在此之前,所有请求的值都通过标准shell字符串操作从所有前导和尾随空格中去除。

我相信这是干净优雅的,尽管我不太确定如果支持的值数组特别大时性能会如何。


-1

这是我对这个问题的看法。这是简短的版本:

function arrayContains() {
        local haystack=${!1}
        local needle="$2"
        printf "%s\n" ${haystack[@]} | grep -q "^$needle$"
}

而长版,我认为它在眼睛上要容易得多。

# With added utility function.
function arrayToLines() {
        local array=${!1}
        printf "%s\n" ${array[@]}
}

function arrayContains() {
        local haystack=${!1}
        local needle="$2"
        arrayToLines haystack[@] | grep -q "^$needle$"
}

例子:

test_arr=("hello" "world")
arrayContains test_arr[@] hello; # True
arrayContains test_arr[@] world; # True
arrayContains test_arr[@] "hello world"; # False
arrayContains test_arr[@] "hell"; # False
arrayContains test_arr[@] ""; # False

我已经很久没有使用bash了,因为我很难理解答案,甚至我自己写的内容:)我不敢相信这个问题在所有时间之后仍在活跃:)
Paolo Tedesco

test_arr=("hello" "world" "two words")
Qwerty

-1

我不得不检查一下另一个脚本/命令生成的ID列表中是否包含一个ID。对我来说,以下工作:

# the ID I was looking for
ID=1

# somehow generated list of IDs
LIST=$( <some script that generates lines with IDs> )
# list is curiously concatenated with a single space character
LIST=" $LIST "

# grep for exact match, boundaries are marked as space
# would therefore not reliably work for values containing a space
# return the count with "-c"
ISIN=$(echo $LIST | grep -F " $ID " -c)

# do your check (e. g. 0 for nothing found, everything greater than 0 means found)
if [ ISIN -eq 0 ]; then
    echo "not found"
fi
# etc.

您也可以像这样缩短/压缩它:

if [ $(echo " $( <script call> ) " | grep -F " $ID " -c) -eq 0 ]; then
    echo "not found"
fi

就我而言,我正在运行jq来为ID列表过滤一些JSON,后来不得不检查我的ID是否在此列表中,这对我来说效果最好。它不适用于该类型的手动创建的数组,LIST=("1" "2" "4")但不适用于以换行符分隔的脚本输出。


PS .:无法评论答案,因为我比较新...


-2

以下代码检查给定值是否在数组中,并返回其从零开始的偏移量:

A=("one" "two" "three four")
VALUE="two"

if [[ "$(declare -p A)" =~ '['([0-9]+)']="'$VALUE'"' ]];then
  echo "Found $VALUE at offset ${BASH_REMATCH[1]}"
else
  echo "Couldn't find $VALUE"
fi

匹配是在完整值上完成的,因此设置VALUE =“ three”将不匹配。


-2

如果您不想迭代,这可能值得调查:

#!/bin/bash
myarray=("one" "two" "three");
wanted="two"
if `echo ${myarray[@]/"$wanted"/"WAS_FOUND"} | grep -q "WAS_FOUND" ` ; then
 echo "Value was found"
fi
exit

摘录自以下网址http//www.thegeekstuff.com/2010/06/bash-array-tutorial/ 我认为这非常聪明。

编辑:您可能可以做:

if `echo ${myarray[@]} | grep -q "$wanted"` ; then
echo "Value was found"
fi

但是后者仅在数组包含唯一值时才有效。在“ 143”中寻找1会产生假阳性的方法。


-2

有点晚了,但是您可以使用:

#!/bin/bash
# isPicture.sh

FILE=$1
FNAME=$(basename "$FILE") # Filename, without directory
EXT="${FNAME##*.}" # Extension

FORMATS=(jpeg JPEG jpg JPG png PNG gif GIF svg SVG tiff TIFF)

NOEXT=( ${FORMATS[@]/$EXT} ) # Formats without the extension of the input file

# If it is a valid extension, then it should be removed from ${NOEXT},
#+making the lengths inequal.
if ! [ ${#NOEXT[@]} != ${#FORMATS[@]} ]; then
    echo "The extension '"$EXT"' is not a valid image extension."
    exit
fi

-2

我想出了这个方法,它只能在zsh中工作,但是我认为一般方法很好。

arr=( "hello world" "find me" "what?" )
if [[ "${arr[@]/#%find me/}" != "${arr[@]}" ]]; then
    echo "found!"
else
    echo "not found!"
fi

仅在每个元素开始${arr[@]/#pattern/}或结束时才从每个元素中取出模式${arr[@]/%pattern/}。这两种替换都可以在bash中使用,但是两者都同时${arr[@]/#%pattern/}只能在zsh中使用。

如果修改后的数组等于原始数组,则它不包含元素。

编辑:

这在bash中有效:

 function contains () {
        local arr=(${@:2})
        local el=$1
        local marr=(${arr[@]/#$el/})
        [[ "${#arr[@]}" != "${#marr[@]}" ]]
    }

替换后,它将比较两个数组的长度。如果数组包含元素,则替换将完全删除它,并且计数将不同。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.