如何在bash中声明二维数组
我想知道如何在bash中声明一个二维数组,然后初始化为0。
在C中看起来像这样:
int a[4][5] = {0};
我怎么给一个元素赋值? 如在C:
a[2][3] = 3;
你可以用哈希来模拟它们,但是需要关心前导零和许多其他的东西。 下一个示范工作,但它是远远不是最佳的解决scheme。
#!/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
上面的例子创build一个带有随机数字的4x5matrix,并将其打印出来,并带有示例结果
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
原则是:创build一个索引是3,4
这样的string的关联数组。 好处:
- 可以用于任意维数组;例如:
30,40,2
三维。 - 语法与数组
${matrix[2,3]}
接近“C”
Bash不支持multidimensional array。
您可以使用间接扩展来模拟它:
#!/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 -aa$idx; do let idx++; done </tmp/some_file
编辑2 :要声明并初始化a0..a3[0..4]
为0
,可以运行:
for i in {0..3}; do eval "declare -aa$i=( $(for j in {0..4}; do echo 0; done) )" done
Bash没有multidimensional array。 但是你可以模拟与关联数组有些相似的效果。 以下是假装用作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
如果你不把数组声明为关联的(使用-A
),上面的方法将不起作用。 例如,如果您省略了declare -A arr
行,则echo
将打印2 3
而不是0 1
,因为0,0
等将被视为算术expression式并评估为0
(右边的值的逗号运算符)。
另一种方法是可以将每一行表示为一个string,即将二维数组映射到一维数组中。 然后,只要您进行编辑,您所需要做的就是解压并重新包装行的string:
# 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
输出:
0 0 0 0 0 0 0 0 0 0 0 0 0 3 0 0 0 0 0 0
你也可以用不那么聪明的方式来解决这个问题
q=() q+=( 1-2 ) q+=( ab ) for set in ${q[@]}; do echo ${set%%-*} echo ${set##*-} done
当然22线解决scheme或间接可能是更好的方式去,为什么不洒在每个地方评估。
一种在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 $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 ### ################
可以简单地定义两个函数来写入($ 4是赋值),并读取一个任意名称($ 1)和索引($ 2和$ 3)的matrix,利用eval和间接引用。
#!/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
如果matrix的每一行是相同的大小,那么你可以简单地使用线性数组和乘法。
那是,
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
这种方法可能会转化为一组函数,但是由于不能将数组传递给函数或从函数返回数组,所以必须使用名称传递,有时候使用eval
。 所以我倾向于在“事情根本就不是想做”的情况下提交multidimensional array。
为了模拟二维数组,我首先加载第一个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
要添加第二列,我需要定义第一列的大小,并计算偏移量中的值
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