Как объявить 2D массив в bash



мне интересно, как объявить 2D-массив в bash, а затем инициализировать до 0.



В C это выглядит так:



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


и как мне присвоить значение элементу? Как в C:



a[2][3] = 3;
474   9  

9 ответов:

вы можете имитировать их, например, с хэшами, но нужно заботиться о ведущих нулях и многих других вещах. Следующая демонстрация работает, но это далеко не оптимальное решение.

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

приведенный выше пример создает матрицу 4x5 со случайными числами и печатает ее транспонированной, с результатом примера

           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

принцип: создание одного ассоциативного массива, где индекс является строкой типа 3,4. Преимущества:

  • вполне возможно использовать для массивов любого размера;) например:30,40,2 для 3-х мерных.
  • синтаксис близок к" C", как массивы ${matrix[2,3]}

Bash не поддерживает многомерные массивы.

вы можете имитировать его, Хотя с помощью косвенного расширения:

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

назначения также возможны с помощью этого метода:

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

изменить 1: чтобы прочитать такой массив из файла, с каждой строкой в строке и значениями, разделенными пробелом, используйте это:

idx=0
while read -a a$idx; do
    let idx++;
done </tmp/some_file

Edit 2: объявить и инициализации a0..a3[0..4] до 0, вы могли бы беги:

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

Bash не имеет многомерного массива. Но вы можете имитировать несколько подобный эффект с ассоциативными массивами. Ниже приведен пример ассоциативного массива, претендующего на использование в качестве многомерного массива:

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

если вы не объявляете массив ассоциативным (с -A), что выше не будет работать. Например, если вы опустите declare -A arr линия,echo печати 2 3 вместо 0 1, потому что 0,0,1,0 и такие будут приняты как арифметика выражение и вычисляется до 0 (значение справа от оператора запятой).

вы также можете подойти к этому гораздо менее умным способом

q=()
q+=( 1-2 )
q+=( a-b )

for set in ${q[@]};
do
echo ${set%%-*}
echo ${set##*-}
done

конечно, 22-строчное решение или косвенность, вероятно, лучший способ пойти и почему бы не посыпать eval каждый, где нужно .

способ моделирования массивов в bash (он может быть адаптирован для любого числа измерений массива):

#!/bin/bash

## The following functions implement vectors (arrays) operations in bash:
## Definition of a vector <v>:
##      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  in the next element position (vector length + 1) in vector 

    local elem_value
    local vector_length
    local elem_name

    eval elem_value=\"$\"
    eval vector_length=$\_0
    if [ -z "$vector_length" ]; then
        vector_length=$((0))
    fi

    vector_length=$(( vector_length + 1 ))
    elem_name=_$vector_length

    eval $elem_name=\"$elem_value\"
    eval _0=$vector_length
}

VectorAddElementDVNext () {
# Vector Add Element Direct Value Next
# Adds the string  in the next element position (vector length + 1) in vector 

    local elem_value
    local vector_length
    local elem_name

    eval elem_value=""
    eval vector_length=$\_0
    if [ -z "$vector_length" ]; then
        vector_length=$((0))
    fi

    vector_length=$(( vector_length + 1 ))
    elem_name=_$vector_length

    eval $elem_name=\"$elem_value\"
    eval _0=$vector_length
}

VectorAddElement () {
# Vector Add Element
# Adds the string contained in the variable  in the position contained in  (variable or direct value) in the vector 

    local elem_value
    local elem_position
    local vector_length
    local elem_name

    eval elem_value=\"$\"
    elem_position=$(())
    eval vector_length=$\_0
    if [ -z "$vector_length" ]; then
        vector_length=$((0))
    fi

    if [ $elem_position -ge $vector_length ]; then
        vector_length=$elem_position
    fi

    elem_name=_$elem_position

    eval $elem_name=\"$elem_value\"
    if [ ! $elem_position -eq 0 ]; then
        eval _0=$vector_length
    fi
}

VectorAddElementDV () {
# Vector Add Element
# Adds the string  in the position  (variable or direct value) in the vector 

    local elem_value
    local elem_position
    local vector_length
    local elem_name

    eval elem_value=""
    elem_position=$(())
    eval vector_length=$\_0
    if [ -z "$vector_length" ]; then
        vector_length=$((0))
    fi

    if [ $elem_position -ge $vector_length ]; then
        vector_length=$elem_position
    fi

    elem_name=_$elem_position

    eval $elem_name=\"$elem_value\"
    if [ ! $elem_position -eq 0 ]; then
        eval _0=$vector_length
    fi
}

VectorPrint () {
# Vector Print
# Prints all the elements names and values of the vector  on sepparate lines

    local vector_length

    vector_length=$((_0))
    if [ "$vector_length" = "0" ]; then
        echo "Vector \"\" is empty!"
    else
        echo "Vector \"\":"
        for ((i=1; i<=$vector_length; i++)); do
            eval echo \"[$i]: \\"$\_$i\\"\"
            ###OR: eval printf \'\%s\\n\' \"[$i]: \\"$\_$i\\"\"
        done
    fi
}

VectorDestroy () {
# Vector Destroy
# Empties all the elements values of the vector 

    local vector_length

    vector_length=$((_0))
    if [ ! "$vector_length" = "0" ]; then
        for ((i=1; i<=$vector_length; i++)); do
            unset _$i
        done
        unset _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 ###
################
# 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[]}"
    tmp[]=
    a[]="${tmp[@]}"
}

# Set a[2][3] = 3
aset 2 3 3

# Show result
for r in "${a[@]}"; do
  echo $r
done

выходы:

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

можно просто определить две функции для записи ($4-назначенное значение) и прочитать матрицу с произвольным именем ($1) и индексами ($2 и $ 3), используя eval и косвенную ссылку.

#!/bin/bash

matrix_write () {
 eval "_""_"=
 # aux="_""_"          # Alternative way
 # let $aux=               # ---
}

matrix_read () {
 aux="_""_"
 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

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

то есть

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

затем ваш a[2][3] = 3 становится

a[2*5+3] = 3

этот подход может быть стоит превратить в набор функций, но так как вы не можете передавать массивы или возвращать массивы из функций, вам придется использовать pass-by-name и иногда eval. Поэтому я склонен подавать многомерные массивы в разделе " вещи bash просто не предназначены Делать."

для моделирования 2-мерного массива я сначала загружаю первые n-элементы (элементы первого столбца)

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

добавить второй столбец, я определяю размер первого столбца и вычислить значения переменной offset

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

Comments

    Ничего не найдено.