获取 Bash 数组中某个值的索引

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/15028567/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-09 23:21:47  来源:igfitidea点击:

Get the index of a value in a Bash array

arraysbashindexing

提问by user137369

I have something in bashlike

我有东西bash

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

And I would like to do something like

我想做类似的事情

echo ${myArray['green']}

Which in this case would output 2. Is this achievable?

在这种情况下将输出2. 这是可以实现的吗?

回答by Steve Walsh

This will do it:

这将做到:

#!/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

Obviously, if you turn this into a function (e.g. get_index() ) - you can make it generic

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

回答by Olaf Dietsche

You must declare your array before use with

您必须在使用前声明您的数组

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

回答by chepner

No. You can only index a simple array with an integer in bash. Associative arrays (introduced in bash4) can be indexed by strings. They don't, however, provided for the type of reverse lookup you are asking for, without a specially constructed associative array.

不可以。您只能索引一个带有整数的简单数组bash。关联数组(在bash4 中介绍)可以通过字符串进行索引。但是,如果没有专门构造的关联数组,它们不会提供您所要求的反向查找类型。

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

回答by PiotrO

There is also one tricky way:

还有一种棘手的方法:

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

And you get 2 Here are references

你得到 2 这里是参考

回答by user3680055

I like that solution:

我喜欢这个解决方案:

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

The variable n will contain the result!

变量 n 将包含结果!

回答by sbts

This is just another way to initialize an associative array as chepner showed. Don't forget that you need to explicitly declareor typset an associative array with -Aattribute.

正如 chepner 所示,这只是另一种初始化关联数组的方法。不要忘记您需要显式declare或打字设置具有-A属性的关联数组。

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

This removes the need to hard code values and makes it unlikely you will end up with duplicates.

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

If you have lots of values to add it may help to put them on separate lines.

如果您有很多值要添加,将它们放在单独的行上可能会有所帮助。

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

Say you want an array of numbers and lowercase letters (eg: for a menu selection) you can also do something like this.

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

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

If you then run

如果你然后运行

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

回答by cmcginty

A little more concise and works in Bash 3.x:

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

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

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

echo $i

回答by srbs

Another tricky one-liner:

另一个棘手的单线:

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

features:

特征:

  • supports elements with spaces
  • returns -1when not found
  • 支持带空格的元素
  • -1未找到时返回

caveats:

警告:

  • requires valueto be non-empty
  • difficult to read
  • 要求value非空
  • 难以阅读


Explanations by breaking it down in execution order:

按执行顺序分解的解释:

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

set array expansion separator (IFS) to a new line char & expand the array

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

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

grep for a match:

grep 匹配:

  • show line numbers (--line-numberor -n)
  • use a fixed string (--fixed-stringsor -F; disables regex)
  • allow for elements starting with a -(--)

    cut -f1 -d:

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

    剪切 -f1 -d:

extract only the line number (format is <line_num>:<matched line>)

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

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

subtract 1 since line numbers are 1-indexed and arrays are 0-indexed

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

  • if $(...)does not match:

    • nothing is returned & the default of 0is used (10#0)
  • if $(...)matches:
    • a line number exists & is prefixed with 10#0; i.e. 10#02, 10#09, 10#014, etc
    • the 10#prefix forces base-10/decimal numbers instead of octal
  • 如果$(...)不匹配:

    • 不返回任何内容 & 使用默认值0( 10#0)
  • 如果$(...)匹配:
    • 存在行号 & 以10#0;为前缀 即10#0210#0910#014,等
    • 10#前缀力碱-10 /十进制数,而不是八进制




Using awkinstead of grep, cut& bash arithmetic:

使用awk代替grep, cut& bash 算法:

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

features:

特征:

  • supports elements with spaces
  • supports empty elements
  • less commands opened in a subshell
  • 支持带空格的元素
  • 支持空元素
  • 在子shell中打开的较少命令

caveats:

警告:

  • returns when not found
  • 未找到时返回


Explanations by breaking it down in execution order:

按执行顺序分解的解释:

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

set array expansion separator (IFS) to a new line char & expand the array

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

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

match the entire line & print the 0-indexed line number

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

  • ${value//\"/\\\"}replaces double quotes in $valuewith escaped versions
  • since we need variable substitution, this segment has more escaping than wanted
  • ${value//\"/\\\"}$value用转义版本替换双引号
  • 由于我们需要变量替换,因此该段的转义比想要的要多

回答by Manish Sharma

This might just work for arrays,

这可能只适用于数组,

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

Output:

输出:

2

If you want to find header index in a tsv file,

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

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

回答by yaccz

In zsh you can do

在 zsh 你可以做

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

see zshparam(1) subsection Subscript Flags

见 zshparam(1) 小节下标标志