array bash com variável no nome

array bash com variável no nome

Agradeço sua ajuda com o seguinte problema:

Estou tentando definir um array que contém uma variável como parte do nome do array, exemplo: Arr_$COUNTER(onde $COUNTERé alterado com base em uma contagem de loops)

Todas as maneiras possíveis que tentei resultaram em um erro, como "substituição incorreta" ou "erro de sintaxe próximo ao token inesperado"

Aqui está todo o fluxo:

  1. Existe um arquivo que contém várias linhas. cada linha tem 6 valores separados por espaço

    10 20 30 40 50 60  
    100 200 300 400 500 600
    
  2. O script destina-se a ler cada linha do arquivo e declará-lo como um array (com o número da linha que é a variável.

  3. como teste, cada valor deverá ser impresso e eventualmente outra função será executada em cada valor.

    #!/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
    

Aqui está o resultado:

Número total de linhas 2

O valor do contador é 1
Campo = 10 20 30 40 50 60
./sort.sh: linha 12: arr$COUNTER[0] = ${arr$COUNTER[0]}: substituição incorreta
O fim

O que deve ser alterado/corrigido para que funcione corretamente?

agradecer !

Responder1

Algumas ideias:

  1. Uma "expansão de parâmetro" de um valor variável (a parte ${...}):

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

    não funciona. Você pode contornar usando eval (mas eu não recomendo):

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

    Essa linha poderia ser escrita assim:

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

    Isso é chamado de indireção (o!) no Bash.

  2. Um problema semelhante acontece com esta linha:

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

    Que deve ser dividido em duas linhas e eval usado:

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

    Novamente, não recomendo usar eval (neste caso).

  3. Como você está lendo o arquivo inteiro na memória do shell, podemos também usar um método mais simples para colocar todas as linhas em um array:

    readarray -t lines <"VALUES_FILE.txt"
    

    Isso deve ser mais rápido do que chamar o awk para cada linha.

Um script com todos os itens acima poderia ser:

#!/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

Porém, ainda assim, o AWK pode ser mais rápido, se pudéssemos executar o que você precisa no AWK.


Um processamento semelhante poderia ser feito no awk. Supondo que os 6 valores serão usados ​​como um IP (4 deles) e os outros dois são um número e uma época.

Apenas um exemplo muito simples de um script 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"

Basta fazer uma nova pergunta com os detalhes.

Responder2

Você pode usar a variável indiretamente, se atribuir o nome e o índice a uma variável:

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

Responder3

A maneira padrão de armazenar dados de matriz multidimensional em uma matriz de dimensão 1 é armazenar cada linha em um deslocamento na matriz.

O elemento (i,j)estará localizado no índice i*m + jonde iestá o índice da linha com base em zero, jé o índice da coluna com base em zero e mé o número de colunas.

Isso também facilita a leitura dos dados, pois podemos simplesmente pegar seu arquivo de entrada, alterar todos os espaços para novas linhas e usar readarray.

Na linha de comando:

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

Podemos descobrir o número de colunas nos dados com

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

E o número de linhas:

$ n=$( wc -l <data )

Então podemos iterar colunas e linhas em um loop duplo como de costume:

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

Para os dados fornecidos, isso geraria

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

Idealmente, você usaria uma linguagem, como Perl, Python ou C, que suporte matrizes multidimensionais. Isto é, se você realmente precisar armazenar todo o conjunto de dados na memória e não puder processá-los linha por linha.

Para processamento linha por linha, awkseria um bom candidato para substituir bash(qualquerseria uma boa candidata para substituir um shell para qualquer tipo de processamento de dados):

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

Responder4

Você pode gerar nomes usando eval, por exemplo,

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

essencialmente citando todos os metacaracteres, exceto aqueles que você deseja avaliar.

Então... se $COUNTERfor 1, seu script serviria

declare -a "arr1=($field)"

Leitura adicional:

informação relacionada