获取Bash数组中值的索引

时间:2013-02-22 16:17:14

标签: arrays bash indexing

我在bash喜欢

myArray=('red' 'orange' 'green')

我想做一些像

这样的事情
echo ${myArray['green']}

在这种情况下会输出2。这可以实现吗?

15 个答案:

答案 0 :(得分:67)

这样做:

#!/bin/bash

my_array=(red orange green)
value='green'

for i in "${!my_array[@]}"; do
   if [[ "${my_array[$i]}" = "${value}" ]]; then
       echo "${i}";
   fi
done

显然,如果你把它变成一个函数(例如get_index()) - 你可以把它变成泛型

答案 1 :(得分:25)

您必须在使用

之前声明您的阵列
declare -A myArray
myArray=([red]=1 [orange]=2 [green]=3)
echo ${myArray['orange']}

答案 2 :(得分:12)

没有。您只能使用bash中的整数索引一个简单数组。关联数组(在bash 4中引入)可以用字符串索引。但是,它们没有提供您要求的反向查找类型,没有特殊构造的关联数组。

$ declare -A myArray
$ myArray=([red]=0 [orange]=1 [green]=2)
$ echo ${myArray[green]}
2

答案 3 :(得分:9)

还有一个棘手的方法:

echo ${myArray[@]/green//} | cut -d/ -f1 | wc -w | tr -d ' '

你得到2 Here are references

答案 4 :(得分:2)

我喜欢这个解决方案:

let "n=(`echo ${myArray[@]} | tr -s " " "\n" | grep -n "green" | cut -d":" -f 1`)-1"

变量n将包含结果!

答案 5 :(得分:2)

这只是chepner显示的另一种初始化关联数组的方法。 不要忘记您需要明确declare或使用-A属性设置关联数组。

i=0; declare -A myArray=( [red]=$((i++)) [orange]=$((i++)) [green]=$((i++)) )
echo ${myArray[green]}
2

这消除了对硬编码值的需要,并且不太可能最终导致重复。

如果要添加许多值,可能有助于将它们放在不同的行上。

i=0; declare -A myArray; 
myArray+=( [red]=$((i++)) )
myArray+=( [orange]=$((i++)) )
myArray+=( [green]=$((i++)) )
echo ${myArray[green]}
2

假设你想要一个数字和小写字母数组(例如:用于菜单选择),你也可以这样做。

declare -a mKeys_1=( {{0..9},{a..z}} );
i=0; declare -A mKeys_1_Lookup; eval mKeys_1_Lookup[{{0..9},{a..z}}]="$((i++))";

如果你再运行

echo "${mKeys_1[15]}"
f
echo "${mKeys_1_Lookup[f]}"
15

答案 6 :(得分:1)

这可能适用于数组,

my_array=(red orange green)
echo "$(printf "%s\n" "${my_array[@]}")" | grep -n '^orange$' | sed 's/:orange//'

输出:

2

如果要在tsv文件中找到标题索引,

head -n 1 tsv_filename | sed 's/\t/\n/g' | grep -n '^header_name$' | sed 's/:header_name//g'

答案 7 :(得分:1)

另一种棘手的单线:

index=$((-1 + 10#0$(IFS=$'\n' echo "${my_array[*]}" | grep --line-number --fixed-strings -- "$value" | cut -f1 -d:)))

功能:

  • 支持带空格的元素
  • 未找到时返回-1

小室:

  • 要求value为非空
  • 难以阅读

按执行顺序分解说明:

IFS=$'\n' echo "${my_array[*]}"

将数组扩展分隔符(IFS)设置为新行char并扩展数组

grep --line-number --fixed-strings -- "$value"

匹配的grep:

  • 显示行号(--line-number-n
  • 使用固定的字符串(--fixed-strings-F;禁用正则表达式)
  • 允许以---)开头的元素

    cut -f1 -d:

仅提取行号(格式为<line_num>:<matched line>

$((-1 + 10#0$(...)))

减去1,因为行号是1索引的,数组是0索引的

  • 如果$(...)不匹配:

    • 不返回任何内容,并且使用默认的010#0
  • 如果$(...)符合:
    • 行号存在并且以10#0为前缀;即10#0210#0910#014
    • 10#前缀强制以10为底的十进制数字,而不是八进制


使用awk代替grepcut和bash算法:

IFS=$'\n'; awk "\$0 == \"${value//\"/\\\"}\" {print NR-1}" <<< "${my_array[*]}"

功能:

  • 支持带空格的元素
  • 支持空元素
  • 在子shell中打开的命令更少

小室:

  • 未找到时返回

按执行顺序分解说明:

IFS=$'\n' [...] <<< "${my_array[*]}"

将数组扩展分隔符(IFS)设置为新行char并扩展数组

awk "\$0 == \"${value//\"/\\\"}\" {print NR-1}"

匹配整行并打印索引为0的行号

  • ${value//\"/\\\"}用转义版本替换$value中的双引号
  • 因为我们需要变量替换,所以该段的转义比需要的要多

答案 8 :(得分:0)

在zsh中你可以做到

xs=( foo bar qux )
echo ${xs[(ie)bar]}

参见zshparam(1)小节下标标志

答案 9 :(得分:0)

更简洁一点,适用于Bash 3.x:

my_array=(red orange green)
value='green'

for i in "${!my_array[@]}"; do
   [[ "${my_array[$i]}" = "${value}" ]] && break
done

echo $i

答案 10 :(得分:0)

这将输出查询的基于0的数组索引(此处为“橙色”)。

echo $(( $(printf "%s\n" "${myArray[@]}" | sed -n '/^orange$/{=;q}') - 1 ))

如果查询未在数组中发生,则以上输出-1

如果查询在数组中多次出现,则上面的命令将输出查询第一次出现的索引。

由于此解决方案调用sed,因此我怀疑它是否可以与该线程中的某些纯bash解决方案竞争以提高效率。

答案 11 :(得分:0)

这显示了一些用于返回数组成员索引的方法。 该数组将不适用的值用于第一个和最后一个索引, 提供从1开始的索引并提供限制。

while循环是一种有趣的迭代方法,它具有截止值, 为了生成数组值的索引, 循环的主体仅包含用于空操作的冒号。 重要的部分是i直到匹配为止的迭代, 或超过可能的匹配。

函数indexof()会将文本值转换为索引。 如果值不匹配,该函数将返回错误代码,该错误代码可能是 在测试中用于执行错误处理。 与数组不匹配的输入值将超过范围限制(-gt, -lt)测试。

有一个测试(主代码)可以循环使用好/坏值,前三行已注释掉,但是请尝试一些变体以查看有趣的结果(lines 1,3 or 2,3 or 4)。我包括了一些考虑错误情况的代码,因为它很有用。

最后一行代码将调用具有已知正确值“绿色”的indexof函数,该函数将回显索引值。

indexof(){
  local s i;

  #   0    1   2     3    4
  s=( @@@ red green blue @o@ )

  while [ ${s[i++]} != $1 ] && [ $i -lt ${#s[@]} ]; do :; done

  [ $i -gt 1 ] && [ $i -lt ${#s[@]} ] || return

  let i--

  echo $i
};# end function indexof

# --- main code ---
echo -e \\033c
echo 'Testing good and bad variables:'
for x in @@@ red pot green blue frog bob @o@;
do
  #v=$(indexof $x) || break
  #v=$(indexof $x) || continue
  #echo $v
  v=$(indexof $x) && echo -e "$x:\t ok" || echo -e "$x:\t unmatched"
done 

echo -e '\nShow the index of array member green:'
indexof green

答案 12 :(得分:0)

myArray=('red' 'orange' 'green')
echo ${myArray[@]}
arrayElementToBeRemoved='orange'
echo "removing element: $arrayElementToBeRemoved"
# Find index of the array element (to be kept or preserved)
let "index=(`echo ${myArray[@]} | tr -s " " "\n" | grep -n "$arrayElementToBeRemoved" | cut -d":" -f 1`)-1"
unset "myArray[$index]"
echo ${myArray[@]}

答案 13 :(得分:0)

我自己也想要类似的东西并避免循环,想出了......

DROP TEMPORARY TABLE IF EXISTS tmp_users_normalized;
CREATE TEMPORARY TABLE tmp_users_normalized
SELECT userkey, username, 1 AS PropertyType, customproperty1 AS Value
FROM users u
UNION ALL
SELECT userkey, username, 2 AS PropertyType, customproperty2 AS Value
FROM users u
UNION ALL
SELECT userkey, username, 3 AS PropertyType, customproperty3 AS Value
FROM users u
;

SELECT *
FROM tmp_users_normalized tun
JOIN ents e ON tun.Value LIKE '%etc%'
;

... 如果找不到元素,则标准输出不会受到污染...

myArray=('red' 'orange' 'green')
declare -p myArray | sed -n "s,.*\[\([^]]*\)\]=\"green\".*,\1,p"

之后我用谷歌搜索,发现了这个问题,并认为我会分享;)

答案 14 :(得分:-1)

简单的解决方案:

my_array=(red orange green)
echo ${my_array[*]} | tr ' ' '\n' | awk '/green/ {print NR-1}'