我有类似的bash
东西
myArray=('red' 'orange' 'green')
我想做类似的事情
echo ${myArray['green']}
在这种情况下会输出2
. 这是可以实现的吗?
这将做到:
#!/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() ) - 你可以让它通用
您必须在使用前声明您的数组
declare -A myArray
myArray=([red]=1 [orange]=2 [green]=3)
echo ${myArray['orange']}
不可以。您只能用 in 中的整数索引一个简单数组bash
。关联数组(在bash
4 中介绍)可以通过字符串进行索引。但是,如果没有专门构造的关联数组,它们不会提供您要求的反向查找类型。
$ declare -A myArray
$ myArray=([red]=0 [orange]=1 [green]=2)
$ echo ${myArray[green]}
2
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
另一个棘手的单线:
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[*]}"
特征:
警告:
找不到时返回通过按执行顺序分解来解释:
IFS=$'\n' [...] <<< "${my_array[*]}"
将数组扩展分隔符 ( IFS
) 设置为新行字符并扩展数组
awk "\$0 == \"${value//\"/\\\"}\" {print NR-1}"
匹配整行并打印以 0 为索引的行号
${value//\"/\\\"}
$value
用转义版本替换双引号这可能只适用于数组,
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'
我喜欢那个解决方案:
let "n=(`echo ${myArray[@]} | tr -s " " "\n" | grep -n "green" | cut -d":" -f 1`)-1"
变量 n 将包含结果!
正如 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
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[@]}
在 zsh 你可以做
xs=( foo bar qux )
echo ${xs[(ie)bar]}
请参阅 zshparam(1) 小节下标标志
这显示了一些返回数组成员索引的方法。该数组对第一个和最后一个索引使用不适用的值,以提供从 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
我想要类似的东西并避免循环,想出了......
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"
$
之后我用谷歌搜索,发现了这个问题并认为我会分享;)
这个输出字符“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
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
这将输出查询的从 0 开始的数组索引(此处为“橙色”)。
echo $(( $(printf "%s\n" "${myArray[@]}" | sed -n '/^orange$/{=;q}') - 1 ))
如果查询未出现在数组中,则上述输出-1
。
如果查询在数组中出现多次,则上面输出查询第一次出现的索引。
由于这个解决方案调用了 sed,我怀疑它能否在效率上与这个线程中的一些纯 bash 解决方案竞争。
简单的解决方案:
my_array=(red orange green)
echo ${my_array[*]} | tr ' ' '\n' | awk '/green/ {print NR-1}'