在Bash脚本中查找数组中字符串的位置

时间:2017-06-22 19:37:49

标签: arrays bash

我正在编写一个shell脚本,我创建了一个包含多个字符串的数组:

array=('string1' 'string2' ... 'stringN')

现在,我有一个保存在变量中的字符串,比如说:

a='stringM'

此字符串是数组的一部分。 我的问题是:如何在数组中找到字符串的位置,而不必逐个检查 for loop

提前致谢

4 个答案:

答案 0 :(得分:1)

没有关于数组的任何其他信息除了检查每个元素之外没有其他解决方案,如果数据被排序,则可以通过二分法进行搜索。 否则可以像散列一样使用另一种结构。

例如,

代替自bash 4以来附加到数组的元素。

declare -A hash
i=0;
for str in string{A..Z}; do
    hash[$str]=$((i++))
done

echo "${hash['stringI']}"

答案 1 :(得分:1)

基本问题是: 为什么你想避免for循环?

  • 语法上的便利性和表现力:您希望以更优雅的方式进行搜索。

  • 效果:您正在寻找最快捷的搜索方式。

<强> TL;博士

出于性能原因,首选外部实用程序解决方案 pure shell 方法;幸运的是,外部效用解决方案通常也是更具表现力的解决方案

  • 对于元素计数,它们会更快。
  • 虽然元素计数会慢一些,但绝对执行时间仍然很低。

以下代码段显示这两个目标的交叉方式(请注意,这两个命令都会返回找到的项目的基于 1 的索引;假设数组元素没有嵌入式换行符):

# Sample input array - adjust the number to experiment
array=( {1..300} )

# Look for the next-to-last item
itmToFind=${array[@]: -1}

# Bash `for` loop
i=1
time for a in "${array[@]}"; do
    [[ $a == "$itmToFind" ]] && { echo "$i"; break; }
    (( ++i ))
done

# Alternative approach: use external utility `grep`
IFS=$'\n' # make sure that "${array[*]}" expands to \n-separated elements
time grep -m1 -Fxn "$itmToFind" <<<"${array[*]}" | cut -d: -f1

grep&#39; -m1选项表示最多搜索一个匹配项; -Fnx表示搜索字词应被视为文字-F),匹配完全(整行,-x ),并为每个匹配前缀行号-n)。

在我的机器上给出数组大小 - 300 - 上述命令执行的操作大致相同:

300

real    0m0.005s
user    0m0.004s
sys 0m0.000s

300

real    0m0.004s
user    0m0.002s
sys 0m0.002s

具体阈值会有所不同,但是:

  • 一般来说,元素数越多,基于外部实用程序的解决方案(例如grep)就越快。

  • 对于元素计数,即使外部实用程序解决方案相对较慢,绝对时间也可能无关紧要。

    < / LI>

要显示极端的一端,以下是1,000,000 - 元素数组(100万个元素)的时序:

1000000

real    0m13.861s
user    0m13.180s
sys 0m0.357s

1000000

real    0m1.520s
user    0m1.411s
sys 0m0.005s

答案 2 :(得分:0)

不确定这是否适合您,或者这是避免 for 循环的最佳方式,但您可以尝试:

$ array=('string1' 'string2' 'string3' 'string4')
$ a='string3'
$ printf "%s\n" "${array[@]}" | grep -m1 -Fxn "$a" | cut -d: -f1
3
$ i=$(( $(printf "%s\n" "${array[@]}" | grep -m1 -Fxn "$a" | cut -d: -f1) - 1 ))
$ echo $i
2

打破它:

printf "%s\n" "${array[@]}"

打印由新行分隔的数组的每个元素,然后我们将其传递给grep以获取$a变量的匹配行号,并使用cut仅获取没有比赛的行号:

printf "%s\n" "${array[@]}" | grep -m1 -Fxn "$a" | cut -d: -f1

最后,从使用arithmetic expansion返回的匹配行号中减去1并将其存储在$i中:

i=$(( $(printf "%s\n" "${array[@]}" | grep -m1 -Fxn "$a" | cut -d: -f1) - 1 ))

答案 3 :(得分:0)

正如其他人已经基于当前数组展示的那样,我可以建议您也可以将数组转换为关联数组,并将字符串作为指向数字的索引。

declare -A array=(['string1']=1
                  ['string2']=2
                  ...
                  ['stringN']=N )

a='stringM'

echo ${array[$a]}