Como declarar um array 2D no bash

Eu estou querendo saber como declarar uma matriz 2D no bash e, em seguida, inicializar para 0.

Em C, é assim:

int a[4][5] = {0}; 

E como eu atribuo um valor a um elemento? Como em C:

 a[2][3] = 3; 

Você pode simulá-los, por exemplo, com hashes, mas precisa se preocupar com os zeros iniciais e muitas outras coisas. A próxima demonstração funciona, mas está longe de ser a solução ideal.

 #!/bin/bash declare -A matrix num_rows=4 num_columns=5 for ((i=1;i< =num_rows;i++)) do for ((j=1;j<=num_columns;j++)) do matrix[$i,$j]=$RANDOM done done f1="%$((${#num_rows}+1))s" f2=" %9s" printf "$f1" '' for ((i=1;i<=num_rows;i++)) do printf "$f2" $i done echo for ((j=1;j<=num_columns;j++)) do printf "$f1" $j for ((i=1;i<=num_rows;i++)) do printf "$f2" ${matrix[$i,$j]} done echo done 

o exemplo acima cria uma matriz 4x5 com números randoms e imprime-a transposta, com o resultado do exemplo

  1 2 3 4 1 18006 31193 16110 23297 2 26229 19869 1140 19837 3 8192 2181 25512 2318 4 3269 25516 18701 7977 5 31775 17358 4468 30345 

O princípio é: Criar um array associativo onde o índice é uma string como 3,4 . Os benefícios:

  • é possível usar matrizes de qualquer dimensão;) como: 30,40,2 para 3 dimensões.
  • a syntax está próxima de "C", como matrizes ${matrix[2,3]}

O Bash não suporta matrizes multidimensionais.

Você pode simular isso usando expansão indireta:

 #!/bin/bash declare -a a0=(1 2 3 4) declare -a a1=(5 6 7 8) var="a1[1]" echo ${!var} # outputs 6 

Atribuições também são possíveis com este método:

 let $var=55 echo ${a1[1]} # outputs 55 

Editar 1 : Para ler uma matriz desse tipo a partir de um arquivo, com cada linha em uma linha e valores delimitados por espaço, use:

 idx=0 while read -aa$idx; do let idx++; done  

Edit 2 : Para declarar e inicializar a0..a3[0..4] para 0 , você pode executar:

 for i in {0..3}; do eval "declare -aa$i=( $(for j in {0..4}; do echo 0; done) )" done 

O Bash não possui um array multidimensional. Mas você pode simular um efeito semelhante com matrizes associativas. A seguir, um exemplo de matriz associativa que pretende ser usada como multidimensional array:

 declare -A arr arr[0,0]=0 arr[0,1]=1 arr[1,0]=2 arr[1,1]=3 echo "${arr[0,0]} ${arr[0,1]}" # will print 0 1 

Se você não declarar o array como associativo (com -A ), o acima não funcionará. Por exemplo, se você omitir a linha declare -A arr , o echo imprimirá 2 3 vez de 0 1 , porque 0,0 , 1,0 e tal será tomado como expressão aritmética e avaliado como 0 (o valor à direita do operador vírgula).

Você também pode abordar isso de uma maneira muito menos inteligente

 q=() q+=( 1-2 ) q+=( ab ) for set in ${q[@]}; do echo ${set%%-*} echo ${set##*-} done 

É claro que uma solução de linha 22 ou indireta é provavelmente o melhor caminho a percorrer e por que não polvilhar eval em todos os lugares para.

Uma maneira de simular matrizes no bash (pode ser adaptado para qualquer número de dimensões de um array):

 #!/bin/bash ## The following functions implement vectors (arrays) operations in bash: ## Definition of a vector : ## v_0 - variable that stores the number of elements of the vector ## v_1..v_n, where n=v_0 - variables that store the values of the vector elements VectorAddElementNext () { # Vector Add Element Next # Adds the string contained in variable $2 in the next element position (vector length + 1) in vector $1 local elem_value local vector_length local elem_name eval elem_value=\"\$$2\" eval vector_length=\$$1\_0 if [ -z "$vector_length" ]; then vector_length=$((0)) fi vector_length=$(( vector_length + 1 )) elem_name=$1_$vector_length eval $elem_name=\"\$elem_value\" eval $1_0=$vector_length } VectorAddElementDVNext () { # Vector Add Element Direct Value Next # Adds the string $2 in the next element position (vector length + 1) in vector $1 local elem_value local vector_length local elem_name eval elem_value="$2" eval vector_length=\$$1\_0 if [ -z "$vector_length" ]; then vector_length=$((0)) fi vector_length=$(( vector_length + 1 )) elem_name=$1_$vector_length eval $elem_name=\"\$elem_value\" eval $1_0=$vector_length } VectorAddElement () { # Vector Add Element # Adds the string contained in the variable $3 in the position contained in $2 (variable or direct value) in the vector $1 local elem_value local elem_position local vector_length local elem_name eval elem_value=\"\$$3\" elem_position=$(($2)) eval vector_length=\$$1\_0 if [ -z "$vector_length" ]; then vector_length=$((0)) fi if [ $elem_position -ge $vector_length ]; then vector_length=$elem_position fi elem_name=$1_$elem_position eval $elem_name=\"\$elem_value\" if [ ! $elem_position -eq 0 ]; then eval $1_0=$vector_length fi } VectorAddElementDV () { # Vector Add Element # Adds the string $3 in the position $2 (variable or direct value) in the vector $1 local elem_value local elem_position local vector_length local elem_name eval elem_value="$3" elem_position=$(($2)) eval vector_length=\$$1\_0 if [ -z "$vector_length" ]; then vector_length=$((0)) fi if [ $elem_position -ge $vector_length ]; then vector_length=$elem_position fi elem_name=$1_$elem_position eval $elem_name=\"\$elem_value\" if [ ! $elem_position -eq 0 ]; then eval $1_0=$vector_length fi } VectorPrint () { # Vector Print # Prints all the elements names and values of the vector $1 on sepparate lines local vector_length vector_length=$(($1_0)) if [ "$vector_length" = "0" ]; then echo "Vector \"$1\" is empty!" else echo "Vector \"$1\":" for ((i=1; i< =$vector_length; i++)); do eval echo \"[$i]: \\\"\$$1\_$i\\\"\" ###OR: eval printf \'\%s\\\n\' \"[\$i]: \\\"\$$1\_$i\\\"\" done fi } VectorDestroy () { # Vector Destroy # Empties all the elements values of the vector $1 local vector_length vector_length=$(($1_0)) if [ ! "$vector_length" = "0" ]; then for ((i=1; i<=$vector_length; i++)); do unset $1_$i done unset $1_0 fi } ################## ### MAIN START ### ################## ## Setting vector 'params' with all the parameters received by the script: for ((i=1; i<=$#; i++)); do eval param="\${$i}" VectorAddElementNext params param done # Printing the vector 'params': VectorPrint params read temp ## Setting vector 'params2' with the elements of the vector 'params' in reversed order: if [ -n "$params_0" ]; then for ((i=1; i<=$params_0; i++)); do count=$((params_0-i+1)) VectorAddElement params2 count params_$i done fi # Printing the vector 'params2': VectorPrint params2 read temp ## Getting the values of 'params2'`s elements and printing them: if [ -n "$params2_0" ]; then echo "Printing the elements of the vector 'params2':" for ((i=1; i<=$params2_0; i++)); do eval current_elem_value=\"\$params2\_$i\" echo "params2_$i=\"$current_elem_value\"" done else echo "Vector 'params2' is empty!" fi read temp ## Creating a two dimensional array ('a'): for ((i=1; i<=10; i++)); do VectorAddElement a 0 i for ((j=1; j<=8; j++)); do value=$(( 8 * ( i - 1 ) + j )) VectorAddElementDV a_$i $j $value done done ## Manually printing the two dimensional array ('a'): echo "Printing the two-dimensional array 'a':" if [ -n "$a_0" ]; then for ((i=1; i<=$a_0; i++)); do eval current_vector_lenght=\$a\_$i\_0 if [ -n "$current_vector_lenght" ]; then for ((j=1; j<=$current_vector_lenght; j++)); do eval value=\"\$a\_$i\_$j\" printf "$value " done fi printf "\n" done fi ################ ### MAIN END ### ################ 

Outra abordagem é que você pode representar cada linha como uma string, ou seja, mapear a matriz 2D em uma matriz 1D. Então, tudo que você precisa fazer é descompactar e reembalar a string da linha sempre que fizer uma edição:

 # Init a 4x5 matrix a=("0 0 0 0 0" "0 0 0 0 0" "0 0 0 0 0" "0 0 0 0 0") function aset { IFS=' ' read -r -a tmp < << "${a[$1]}" tmp[$2]=$3 a[$1]="${tmp[@]}" } # Set a[2][3] = 3 aset 2 3 3 # Show result for r in "${a[@]}"; do echo $r done 

Saídas:

 0 0 0 0 0 0 0 0 0 0 0 0 0 3 0 0 0 0 0 0 

Pode-se simplesmente definir duas funções para escrever ($ 4 é o valor atribuído) e ler uma matriz com nome arbitrário ($ 1) e índices ($ 2 e $ 3) explorando eval e indiretamente referenciamento.

 #!/bin/bash matrix_write () { eval $1"_"$2"_"$3=$4 # aux=$1"_"$2"_"$3 # Alternative way # let $aux=$4 # --- } matrix_read () { aux=$1"_"$2"_"$3 echo ${!aux} } for ((i=1;i<10;i=i+1)); do for ((j=1;j<10;j=j+1)); do matrix_write a $i $j $[$i*10+$j] done done for ((i=1;i<10;i=i+1)); do for ((j=1;j<10;j=j+1)); do echo "a_"$i"_"$j"="$(matrix_read a $i $j) done done 

Se cada linha da matriz é do mesmo tamanho, então você pode simplesmente usar uma matriz linear e multiplicação.

Isso é,

 a=() for (( i=0; i<4; ++i )); do for (( j=0; j<5; ++j )); do a[i*5+j]=0 done done 

Então o seu a[2][3] = 3 torna-se

 a[2*5+3] = 3 

Essa abordagem pode valer a pena se transformar em um conjunto de funções, mas como você não pode passar matrizes para ou retornar matrizes de funções, você teria que usar pass-by-name e, às vezes, eval . Então, eu tenho tendência para arquivar matrizes multidimensionais em "coisas bash é simplesmente não significa fazer".

Para simular um array bidimensional, primeiro carrego os primeiros n-elementos (os elementos da primeira coluna)

 local pano_array=() i=0 for line in $(grep "filename" "$file") do url=$(extract_url_from_xml $line) pano_array[i]="$url" i=$((i+1)) done 

Para adicionar a segunda coluna, defino o tamanho da primeira coluna e calculo os valores em uma variável de deslocamento

 array_len="${#pano_array[@]}" i=0 while [[ $i -lt $array_len ]] do url="${pano_array[$i]}" offset=$(($array_len+i)) found_file=$(get_file $url) pano_array[$offset]=$found_file i=$((i+1)) done