75

我有类似的bash东西

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

我想做类似的事情

echo ${myArray['green']}

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

4

17 回答 17

93

这将做到:

#!/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() ) - 你可以让它通用

于 2013-02-22T16:30:09.900 回答
34

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

declare -A myArray
myArray=([red]=1 [orange]=2 [green]=3)
echo ${myArray['orange']}
于 2013-02-22T16:32:01.657 回答
16

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

$ declare -A myArray
$ myArray=([red]=0 [orange]=1 [green]=2)
$ echo ${myArray[green]}
2
于 2013-02-22T16:28:14.557 回答
16

还有一种棘手的方法:

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

你得到 2 这里是参考

于 2015-06-17T15:00:09.263 回答
7

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

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

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

echo $i
于 2018-05-23T06:39:20.427 回答
5

另一个棘手的单线:

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) 设置为新行字符并扩展数组

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

grep 匹配:

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

    剪切-f1 -d:

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

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

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

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

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


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

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

特征:

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

警告:

  • 找不到时返回

通过按执行顺序分解来解释:

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

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

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

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

  • ${value//\"/\\\"}$value用转义版本替换双引号
  • 因为我们需要变量替换,所以这个段的转义比想要的多
于 2019-01-03T08:48:45.617 回答
3

这可能只适用于数组,

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'
于 2017-04-12T11:28:47.500 回答
2

我喜欢那个解决方案:

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

变量 n 将包含结果!

于 2015-08-13T18:08:54.477 回答
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
于 2015-11-15T20:01:49.153 回答
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[@]}
于 2020-09-22T00:50:35.907 回答
0

在 zsh 你可以做

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

请参阅 zshparam(1) 小节下标标志

于 2017-06-18T04:02:16.833 回答
0

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

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

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

有一个循环好/坏值的测试(主代码),前 3 行被注释掉,但尝试一些变化来查看有趣的结果(lines 1,3 or 2,3 or 4)。我包含了一些考虑错误条件的代码,因为它很有用。

最后一行代码调用具有已知良好值“green”的函数 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
于 2020-08-03T01:22:00.120 回答
0

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

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

...如果找不到该元素,则 stdout 不会受到污染...

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

$ declare -p myArray | sed -n "s,.*\[\([^]]*\)\]=\"gren\".*,\1,p"
$

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

于 2021-01-11T19:04:08.527 回答
0

这个输出字符“Molly”的基于 1 的 NEUROMANCER 索引;)

get_index() {

  declare -n dummy_array="$1"
  # alternative: split read -ra array <<< "${dummy_array[@]}"
  local array=( "${dummy_array[@]}" )
  # alternative: local value; value="$( for dummy_value; do true; done; echo "$dummy_value" )"
  local value=$2
  local length="${#array[@]}"
  local i=0
  
  while (( i < length ))
  do
    if [ "${array[$i]}" = "$value" ]
    then echo $(( i + 1 )); return 0
    fi; (( i++ ))
  done
  
  echo "$2 not found beneath $1"
  exit 1

}

NEUROMANCER=(Case Molly Riviera)
get_index NEUROMANCER Molly
get_index NEUROMANCER 'John Doe'

如果你然后运行:

$ bash script.sh
2
John Doe not found beneath NEUROMANCER

于 2021-11-09T22:26:29.397 回答
0
function array_indexof() {
  [ $# -lt 2 ] && return 1
  local a=("$@")
  local v="${a[-1]}"
  unset a[-1]
  local i
  for i in ${!a[@]}; do
    if [ "${a[$i]}" = "$v" ]; then
      echo $i
      return 0 # stop after first match
    fi
  done
  return 1
}

a=(a b c d)
i=$(array_indexof "${a[@]}" d)
echo $i # 3
于 2022-01-20T21:34:23.003 回答
0

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

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

如果查询未出现在数组中,则上述输出-1

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

由于这个解决方案调用了 sed,我怀疑它能否在效率上与这个线程中的一些纯 bash 解决方案竞争。

于 2020-05-15T12:16:10.317 回答
-2

简单的解决方案:

my_array=(red orange green)
echo ${my_array[*]} | tr ' ' '\n' | awk '/green/ {print NR-1}'
于 2019-03-02T10:20:41.550 回答