array bash com variável no nome

3

Eu aprecio sua ajuda com o seguinte problema:

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

Todas as maneiras possíveis que tentei apareceram com 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 uma matriz (com o número da linha que é a variável.

  3. como teste, cada valor deve 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:

Total number of lines 2

Counter value is 1
Field = 10 20 30 40 50 60
./sort.sh: line 12: arr$COUNTER[0] = ${arr$COUNTER[0]}: bad substitution
The End

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

obrigado!

    
por AlonCo 15.02.2016 / 23:23

3 respostas

1

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 funcionará. 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 indirecçã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 \)
    

    Mais uma vez, não recomendo usar eval (neste caso).

  3. Como você está lendo todo o arquivo 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

No entanto, ainda assim, o AWK pode ser mais rápido, se pudermos executar o que você precisa no AWK.

Um processamento similar pode ser feito no awk. Assumindo que os 6 valores serão usados como um IP (4 deles) e os outros dois são um número e um tempo de época.

Apenas uma amostra 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.

    
por 16.02.2016 / 06:44
2

Você pode usar a variável indirecção, se você atribuir o nome e o índice a uma variável:

s="arr$COUNTER[0]"
echo "arr$COUNTER[0] = ${!s}"
    
por 15.02.2016 / 23:31
1

Você pode gerar nomes usando eval , por exemplo,

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

essencialmente citando todos os meta-caracteres, exceto os que você deseja avaliar.

Então ... se $COUNTER for 1, seu script faria

declare -a "arr1=($field)"

Leitura adicional:

por 15.02.2016 / 23:28