массив bash с переменной в имени

массив bash с переменной в имени

Буду признателен за помощь в решении следующей проблемы:

Я пытаюсь задать массив, содержащий переменную как часть имени массива, например: Arr_$COUNTER(где $COUNTERизменяется в зависимости от количества циклов)

Все возможные способы, которые я пробовал, приводили к ошибке, например, «неправильная замена» или «синтаксическая ошибка рядом с неожиданным токеном».

Вот весь процесс:

  1. Есть один файл, содержащий несколько строк. Каждая строка имеет 6 значений, разделенных пробелом.

    10 20 30 40 50 60  
    100 200 300 400 500 600
    
  2. Скрипт предназначен для считывания каждой строки из файла и объявления ее как массива (с номером строки, который является переменной).

  3. В качестве теста каждое значение должно быть выведено на печать, а затем для каждого значения будет выполнена другая функция.

    #!/bin/bash
    COUNTER=1
    LINES=`wc -l VALUES_FILE.txt | awk '{print $1}'`
    echo "Total number of lines "$LINES
    echo
    while [ $COUNTER -le $LINES ]
    do
    echo "Counter value is $COUNTER"
    field=`awk "NR == $COUNTER" VALUES_FILE.txt`
    echo "Field = $field"
    declare -a "arr$COUNTER=($field)"
    echo "arr$COUNTER[0] = ${arr$COUNTER[0]}"
    echo "arr$COUNTER[1] = ${arr$COUNTER[1]}"
    echo "arr$COUNTER[2] = ${arr$COUNTER[2]}"
    echo "arr$COUNTER[3] = ${arr$COUNTER[3]}"
    echo "arr$COUNTER[4] = ${arr$COUNTER[4]}"
    echo "arr$COUNTER[5] = ${arr$COUNTER[5]}"
    let COUNTER=COUNTER+1
    echo
    done
    echo "The End"
    echo
    

Вот результат:

Общее количество строк 2

Значение счетчика равно 1
Поле = 10 20 30 40 50 60
./sort.sh: строка 12: arr$COUNTER[0] = ${arr$COUNTER[0]}: плохая замена
Конец

Что следует изменить/исправить, чтобы все работало правильно?

благодарить !

решение1

Некоторые идеи:

  1. «Расширение параметров» значения переменной (часть ${...}):

    echo "arr$COUNTER[0] = ${arr$COUNTER[0]}"
    

    не будет работать. Вы можете обойти это с помощью eval (но я не рекомендую):

    eval echo "arr$COUNTER[0] = \${arr$COUNTER[0]}"
    

    Эту строку можно было бы записать так:

    i="arr$COUNTER[0]"; echo "$i = ${!i}"
    

    В Bash это называется косвенным обращением (!).

  2. Похожая проблема возникает и в этой строке:

    declare -a "arr$COUNTER=($field)"
    

    Который следует разделить на две строки и использовать eval:

    declare -a "arr$COUNTER"
    eval arr$COUNTER\=\( \$field \)
    

    Опять же, я не рекомендую использовать eval (в данном случае).

  3. Поскольку вы считываете весь файл в память оболочки, мы можем использовать более простой метод для помещения всех строк в массив:

    readarray -t lines <"VALUES_FILE.txt"
    

    Это должно быть быстрее, чем вызывать awk для каждой строки.

Скрипт со всем вышеперечисленным может быть таким:

#!/bin/bash
valfile="VALUES_FILE.txt"

readarray -t lines <"$valfile"             ### read all lines in.

line_count="${#lines[@]}"
echo "Total number of lines $line_count"

for ((l=0;l<$line_count;l++)); do
    echo "Counter value is $l"             ### In which line are we?
    echo "Field = ${lines[l]}"             ### kepth only to help understanding.
    k="arr$l"                              ### Build the variable arr$COUNTER
    IFS=" " read -ra $k <<<"${lines[l]}"   ### Split into an array a line.
    eval max=\${#$k[@]}                    ### How many elements arr$COUNTER has?
    #echo "field $field and k=$k max=$max" ### Un-quote to "see" inside.
    for ((j=0;j<$max;j++)); do             ### for each element in the line.
        i="$k[$j]"; echo "$i = ${!i}"      ### echo it's value.
    done
done
echo "The End"
echo

Однако AWK все равно может быть быстрее, если мы сможем выполнить то, что вам нужно, в AWK.


Аналогичную обработку можно выполнить в awk. Предполагая, что 6 значений будут использоваться как IP (4 из них), а два других — это число и время эпохи.

Очень простой пример скрипта AWK:

#!/bin/sh
valfile="VALUES_FILE.txt"
awk '
NF==6 { printf ( "IP: %s.%s.%s.%s\t",$1,$2,$3,$4)
        printf ( "number: %s\t",$5+2)
        printf ( "epoch: %s\t",$6)
        printf ( "\n" )
    }
' "$valfile"

Просто создайте новый вопрос с подробностями.

решение2

Вы можете использовать косвенную адресацию переменной, если присвоите переменной и имя, и индекс:

s="arr$COUNTER[0]"
echo "arr$COUNTER[0] = ${!s}"

решение3

Стандартный способ хранения данных многомерного массива в массиве размерности 1 — это сохранение каждой строки со смещением в массиве.

Элемент (i,j)будет расположен по индексу, i*m + jгде i— индекс строки (отсчитываемый от нуля), j— индекс столбца (отсчитываемый от нуля), а m— количество столбцов.

Это также упрощает чтение данных, поскольку мы можем просто взять ваш входной файл, заменить все пробелы на символы новой строки и использовать readarray.

В командной строке:

$ readarray -t arr < <( tr -s ' ' '\n' <data )
$ printf '%s\n' "${arr[@]}"
10
20
30
40
50
60
100
200
300
400
500
600

Мы можем вычислить количество столбцов в данных с помощью

$ m=$( awk '{ print NF; exit }' <data )

И количество строк:

$ n=$( wc -l <data )

Затем мы можем перебрать столбцы и строки в двойном цикле, как обычно:

for (( i = 0; i < n; ++i )); do
    for (( j = 0; j < m; ++j )); do
        printf '%4d' "${arr[i*m + j]}"
    done
    printf '\n'
done

Для данных это сгенерирует

  10  20  30  40  50  60
 100 200 300 400 500 600

В идеале вы бы использовали язык, например Perl, Python или C, который поддерживает многомерные массивы. То есть, если вам действительно нужно хранить весь набор данных в памяти и вы не можете обрабатывать его построчно.

Для построчной обработки awkхорошим кандидатом будет замена bash(любойязык был бы хорошим кандидатом на замену оболочки для любого вида обработки данных):

awk '{ for (i = 1; i <= NF; ++i) printf("%4d", $i); printf("\n") }' data

решение4

Вы можете генерировать имена, используя eval, например,

eval declare -a '"arr'$COUNTER'=($field)"'

по сути, цитирование всех метасимволов, за исключением тех, которые вы хотите оценить.

Итак... если $COUNTER1, ваш скрипт будет работать

declare -a "arr1=($field)"

Дальнейшее чтение:

Связанный контент