it-swarm.com.ru

Получить индекс значения в массиве Bash

У меня есть что-то в bash как

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

И я хотел бы сделать что-то вроде

echo ${myArray['green']}

Который в этом случае будет выводить 2. Это достижимо?

39
user137369

Это сделает это:

#!/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 ()) - вы можете сделать ее универсальной

60
Steve Walsh

Вы должны объявить свой массив перед использованием с 

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

Нет. Вы можете индексировать только простой массив с целым числом в bash. Ассоциативные массивы (введенные в bash 4) могут индексироваться строками. Однако они не обеспечивают требуемый тип обратного просмотра без специально созданного ассоциативного массива.

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

Есть еще один хитрый способ:

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

И вы получаете 2 Вот ссылки

9
PiotrO

Мне нравится это решение:

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

Переменная n будет содержать результат!

2
user3680055

Это просто еще один способ инициализации ассоциативного массива, как показал Чепнер . Не забывайте, что вам нужно явно указать 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
2
sbts

Это может просто работать для массивов,

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'
1
Manish Sharma

Еще один хитрый однострочник:

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

  • если $(...) не совпадает:

    • ничего не возвращается и используется значение по умолчанию 0 (10#0)
  • если $(...) соответствует:
    • номер строки существует и имеет префикс 10#0; т.е. 10#02, 10#09, 10#014 и т. д.
    • префикс 10# устанавливает восьмеричные/десятичные числа вместо восьмеричных


Использование awk вместо grep, cut & bash arithmetic:

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

функции:

  • поддерживает элементы с пробелами
  • поддерживает пустые элементы
  • меньше команд, открытых в подоболочке

предостережения:

  • возвращает когда не найден

Пояснения, разбив его по порядку исполнения:

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

установите разделитель расширения массива (IFS) на новую строку char и разверните массив

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

сопоставить всю строку и вывести номер строки с 0 индексами

  • ${value//\"/\\\"} заменяет двойные кавычки в $value на экранированные версии
  • так как нам нужна замена переменных, этот сегмент имеет больше экранирования, чем хотел
1
srbs

Немного более лаконично и работает в Bash 3.x:

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

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

echo $i
0
cmcginty

В зш можно сделать

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

см. zshparam (1) подраздел Subscript Flags

0
yaccz

Простое решение:

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