Linux 如何在bash中声明二维数组

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/16487258/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-06 22:55:00  来源:igfitidea点击:

How to declare 2D array in bash

linuxbashshell

提问by Anis_Stack

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

我想知道如何在 bash 中声明一个二维数组,然后初始化为 0。

In C it looks like this:

在 C 中,它看起来像这样:

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

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

以及如何为元素赋值?如在 C 中:

a[2][3] = 3;

采纳答案by jm666

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

上面的例子创建了一个带有随机数的 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

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

原理是:创建一个关联数组,其中索引是像3,4. 好处:

  • it's possible to use for any-dimension arrays ;) like: 30,40,2for 3 dimensional.
  • the syntax is close to "C" like arrays ${matrix[2,3]}
  • 可以用于任意维数组 ;) 如:30,40,2用于 3 维。
  • 语法接近于“C”之类的数组 ${matrix[2,3]}

回答by Sir Athos

Bash does not support multidimensional arrays.

Bash 不支持多维数组。

You can simulate it though by using indirect expansion:

您可以通过使用间接扩展来模拟它:

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

Assignments are also possible with this method:

也可以使用此方法进行赋值:

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

Edit 1: To read such an array from a file, with each row on a line, and values delimited by space, use this:

编辑 1:要从文件中读取这样的数组,每一行都在一行上,值以空格分隔,请使用:

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

Edit 2: To declare andinitialize a0..a3[0..4]to 0, you could run:

编辑 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

回答by James

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 .

当然,一个 22 行的解决方案或间接方法可能是更好的方法,为什么不把 eval 洒到每个地方。

回答by Jahid

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:

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

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

如果您不将数组声明为关联(与-A),则上述方法将不起作用。例如,如果您省略该declare -A arr行,echo将打印2 3而不是0 1, 因为0,01,0并且此类将被视为算术表达式并计算为0(逗号运算符右侧的值)。

回答by Jahid

A way to simulate arrays in bash (it can be adapted for any number of dimensions of an array):

一种在 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 ###
################

回答by Stephen Quan

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=
  col=
  value=
  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

回答by Adolfo Avella

One can simply define two functions to write ($4 is the assigned value) and read a matrix with arbitrary name ($1) and indexes ($2 and $3) exploiting eval and indirect referencing.

可以简单地定义两个函数来写入($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

回答by Mark Reed

If each row of the matrix is the same size, then you can simply use a linear array and multiplication.

如果矩阵的每一行大小相同,那么您可以简单地使用线性数组和乘法。

That is,

那是,

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

Then your a[2][3] = 3becomes

那么你的a[2][3] = 3变成

a[2*5+3] = 3

This approach might be worth turning into a set of functions, but since you can't pass arrays to or return arrays from functions, you would have to use pass-by-name and sometimes eval. So I tend to file multidimensional arrays under "things bash is simply Not Meant To Do".

这种方法可能值得转化为一组函数,但由于您无法将数组传递给函数或从函数返回数组,因此您必须使用按名称传递,有时还必须使用eval. 因此,我倾向于将多维数组归档在“事情 bash 根本不打算做”下。

回答by kjell moens

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

为了模拟二维数组,我首先加载前 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

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

回答by vorakl

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.

Mark Reed 为二维数组(矩阵)提出了一个非常好的解决方案!它们总是可以在一维数组(向量)中转换。尽管 Bash 没有对 2D 数组的原生支持,但围绕上述原则创建一个简单的 ADT 并不难。

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)

这是一个没有参数检查等的准系统示例,只是为了使解决方案清晰:数组的大小设置为实例中的两个第一个元素(实现矩阵 ADT 的 Bash 模块的文档, 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=                                                          
    declare -i width= height=                                                
    shift 3;                                                                    

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

matrix_get() {                                                                  
    # matrix_get instance x y

    declare -n self=                                                          
    declare -i x= y=                                                        
    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=                                                          
    declare -i x= y=                                                        
    declare data=""                                                           
    declare -i width=${self[0]} height=${self[1]}                                

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

matrix_destroy() {                                                                     
    # matrix_destroy instance

    declare -n self=                                                          
    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