2016-09-28 2 views
1

В отличие от ksh, что позволяет многомерные массивы, bash (4.2 или ниже) не допускает многомерные массивы. Я работаю над этим, имея разделитель в элементах, а затем каждый элемент первого массива может быть сделан в самом массиве. Foe пример:Обходное решение для ограничения размера мономерного размера bash

GPIO=("in:down:0:22:1-1.4:17:usb:usb port 3 mgmt button" 
"in:down:0:13:1-1.5:18:usb:usb port 4 mgmt button") 

затем получить доступ к третьему элементу первой строки я сделать что-то вроде этого:

eval $(echo "ROW0=($(echo "${GPIO[0]}" | awk -F: '{ for(i=1;i<=NF;i++) printf("\"%s\" ",$i);}'))") 
echo ${ROW0[2]} 

мне не нравится это делать, но я не нашел более аккуратный путь вокруг проблемы. Есть ли более аккуратный и эффективный способ решения проблемы, все еще приближаясь к нему, как массив?

Существует также обходной путь с использованием ассоциативных массивов и добавлением поддельного второго индекса в индексе элемента:

declare -A FAKE2DIMARRAY 
FAKE2DIMARRAY=(
[0,0]="first row first element" 
[0,1]="first row second element" 
[1,0]="second row first element" 
[1,1]="second row second element" 
) 

Хотя это решение очень аккуратно и эффективно это делает определение массива беспорядка, если он имеет много строк и элементов. Например, мой полный массив GPIO на самом деле равен 16x8, и для этого потребуется 128 строк и заботиться о том, чтобы правильно индексировать каждую строку ... в этом случае это тоже не оптимально.

+0

увидеть, если http://stackoverflow.com/questions/11233825/multi-dimensional-arrays-in-bash и http://stackoverflow.com/questions/16487258/how-to-declare-2d -array-in-bash help ... и это опечатка в вашем названии? 'mono' вместо' multi' – Sundeep

+1

Прочтите название как «обходное решение для ограничения размера мономерного размера bash» Обе ссылки используют фальшивую многомерную ... вторая имеет интересный подход, хотя ... но ограничивает что вы помещаете в строки. – louigi600

+2

Необходимые вложенные структуры данных - очень хороший признак того, что вам нужно начать использовать другой язык. – chepner

ответ

2

Не так сложно создать автоматическое создание (поддельного) многомерного fullarray. Чтобы извлечь индексы из массива: посмотрите на конец.

Используйте строки, как вы их сейчас создать несколько массивов:

IFS=: read -a arr0 <<<"in:down:0:22:1-1.4:17:usb:usb port 3 mgmt button" 
IFS=: read -a arr1 <<<"in:down:0:13:1-1.5:18:usb:usb port 4 mgmt button" 

Просто сделать их начать с тем же именем, arr в этом примере.

Затем автоматическая система, которая будет принимать все переменные, имена которых начинаются с arr для строк.
и подсчет значений в таких массивов в виде столбцов:

#!/bin/bash 
IFS=: read -a row1 <<<"in:down:0:22:1-1.4:17:usb:usb port 3 mgmt button" 
IFS=: read -a row2 <<<"in:down:0:13:1-1.5:18:usb:usb port 4 mgmt button" 

#echo "one ${row1[@]}" 
#echo "two ${row2[@]}" 

declare -A fullarray 
# Note that ${!row*} lists all the variables that start with arr. 
for hrow in ${!row*}; do 
    #echo "row $hrow" 
    for ((column=0; column<${#row1[@]}; column++)); do 
     #echo "column $column" 
     indirect=$hrow[$column] 
     #echo "indirect $indirect ${!indirect}" 
     fullarray[${hrow#row},$column]=${!indirect} 
    done 
done 

declare -p "fullarray" 

Un комментарий эхо (ы), чтобы увидеть, как строится массив.

Как видно из вывода declare -p fullarray, переменная имеет все значения в массиве (hrow, column), и к каждому значению можно получить доступ к fullarray[$hrow,$column], если строка и столбец являются номерами от 0 и выше.
В этом случае сохраняйте $ в переменных, так как нам требуется строковое представление индексов (а не их числовой эквивалент).


Конечно, я предполагаю, что назначение массиву каждой строки уже решит вашу проблему без необходимости использования awk.

Это зависит только при использовании:

indirect=arr$row[$column] 
${!indirect} 

достаточно хорошо для вас. :-)


Чтобы извлечь индексы из полного массива, если это необходимо, вы можете сделать это в три этапа.Сначала получите все индексы из fullarray:

allind=(${!fullarray}) 

И, как allind будет массив сам по себе, вы могли бы использовать запятую в качестве разделителя, чтобы извлечь каждую часть:

rowind=(${allind[@]//,*}) 

В качестве rowind будет содержат все индексы строк для всех столбцов, значения будут повторяться. Просто сортировать и удалять повторы:

rowind=($(printf '%s\n' "${rowind[@]}"|sort -u)) 

И, если вы хотите, посмотрите на определение индексов строк:

declare -p rowind 

Тот же процесс (за исключением того, allind уже определен) для колонн :

allind=(${!fullarray[@]}) 
columnind=(${allind[@]//*,}) 
columnind=($(printf '%s\n' "${columnind[@]}"|sort -u)) 
declare -p columnind 
+0

Приятно Мне это нравится ... и он избегает всех труб при каждом доступе к данным строки (что является производительностью убийца). Вероятно, я префишу имена массивов для каждой строки «ROW» вместо «arr». Может также обойтись без чтения -a и просто объявить 16 строк, а затем превратить их в поддельный многоадресный массив ... Я думаю, что я поеду для этого ... будет легче прочитать конфигурационный файл (полученный в скрипт). – louigi600

+0

Я очистил сценарий прошлой ночью (теперь GPIO создается из PORT flike fullarray), а я импровизировал "для PORT в $ (tr -d" PORT "<<< $ {! PORT *})" , чтобы получить список номеров портов из массивов, содержащих данные строки. Мне было интересно, можно ли это сделать чище? Или даже непосредственно из поддельной многомерной массива? Есть ли способ сделать что-то вроде $ {! GPIO [*, 0]} , чтобы получить все имена столбцов первого столбца каждой строки? – louigi600

+0

$ {! PORT *} woild имеют все имена переменных, начинающиеся с PORT $ {! Fullarray [*]} будут иметь все индексы в fullarray. Я хотел бы иметь подмножество всех индексов, потому что это поддельный multidim (для эмулирования возвращающих только имена столбцов первого столбца каждой строки). – louigi600