How to declare 2D array in bash

2020-01-22 18:10发布

I'm wondering how to declare a 2D array in bash and then initialize to 0.

In C it looks like this:

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

And how do I assign a value to an element? As in C:

a[2][3] = 3;

标签: linux bash shell
11条回答
兄弟一词,经得起流年.
2楼-- · 2020-01-22 18:29

Mark Reed suggested a very good solution for 2D arrays (matrix)! They always can be converted in a 1D array (vector). Although Bash doesn't have a native support for 2D arrays, it's not that hard to create a simple ADT around the mentioned principle.

Here is a barebone example with no argument checks, etc, just to keep the solution clear: the array's size is set as two first elements in the instance (documentation for the Bash module that implements a matrix ADT, https://github.com/vorakl/bash-libs/blob/master/src.docs/content/pages/matrix.rst )

#!/bin/bash

matrix_init() {
    # matrix_init instance x y data ...

    declare -n self=$1                                                          
    declare -i width=$2 height=$3                                                
    shift 3;                                                                    

    self=(${width} ${height} "$@")                                               
}                                                                               

matrix_get() {                                                                  
    # matrix_get instance x y

    declare -n self=$1                                                          
    declare -i x=$2 y=$3                                                        
    declare -i width=${self[0]} height=${self[1]}                                

    echo "${self[2+y*width+x]}"                                                 
}                                                                               

matrix_set() {                                                                  
    # matrix_set instance x y data

    declare -n self=$1                                                          
    declare -i x=$2 y=$3                                                        
    declare data="$4"                                                           
    declare -i width=${self[0]} height=${self[1]}                                

    self[2+y*width+x]="${data}"                                                 
}                                                                               

matrix_destroy() {                                                                     
    # matrix_destroy instance

    declare -n self=$1                                                          
    unset self                                                                  
}

# my_matrix[3][2]=( (one, two, three), ("1 1" "2 2" "3 3") )
matrix_init my_matrix \                                                         
        3 2 \                                                               
        one two three \                                                     
        "1 1" "2 2" "3 3"

# print my_matrix[2][0]
matrix_get my_matrix 2 0

# print my_matrix[1][1]
matrix_get my_matrix 1 1

# my_matrix[1][1]="4 4 4"
matrix_set my_matrix 1 1 "4 4 4"                                                

# print my_matrix[1][1]
matrix_get my_matrix 1 1                                                        

# remove my_matrix
matrix_destroy my_matrix
查看更多
霸刀☆藐视天下
3楼-- · 2020-01-22 18:34

Bash doesn't have multi-dimensional array. But you can simulate a somewhat similar effect with associative arrays. The following is an example of associative array pretending to be used as multi-dimensional 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

If you don't declare the array as associative (with -A), the above won't work. For example, if you omit the declare -A arr line, the echo will print 2 3 instead of 0 1, because 0,0, 1,0 and such will be taken as arithmetic expression and evaluated to 0 (the value to the right of the comma operator).

查看更多
Viruses.
4楼-- · 2020-01-22 18:36

You can simulate them for example with hashes, but need care about the leading zeroes and many other things. The next demonstration works, but it is far from optimal solution.

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

the above example creates a 4x5 matrix with random numbers and print it transposed, with the example result

           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

The principle is: Creating one associative array where the index is an string like 3,4. The benefits:

  • it's possible to use for any-dimension arrays ;) like: 30,40,2 for 3 dimensional.
  • the syntax is close to "C" like arrays ${matrix[2,3]}
查看更多
5楼-- · 2020-01-22 18:38

For simulating a 2-dimensional array, I first load the first n-elements (the elements of the first column)

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

To add the second column, I define the size of the first column and calculate the values in an offset variable

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
查看更多
不美不萌又怎样
6楼-- · 2020-01-22 18:47

You can also approach this in a much less smarter fashion

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

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

of course a 22 line solution or indirection is probably the better way to go and why not sprinkle eval every where to .

查看更多
【Aperson】
7楼-- · 2020-01-22 18:50

Another approach is you can represent each row as a string, i.e. mapping the 2D array into an 1D array. Then, all you need to do is unpack and repack the row's string whenever you make an edit:

# Init a 4x5 matrix
a=("00 01 02 03 04" "10 11 12 13 14" "20 21 22 23 24" "30 31 32 33 34")

aset() {
  row=$1
  col=$2
  value=$3
  IFS=' ' read -r -a tmp <<< "${a[$row]}"
  tmp[$col]=$value
  a[$row]="${tmp[@]}"
}

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

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

Outputs:

00 01 02 03 04
10 11 12 13 14
20 21 22 9999 24
30 31 32 33 34
查看更多
登录 后发表回答