我想知道如何在bash中声明一个2D数组,然后初始化为0。
在C中它看起来像这样:
int a[4][5] = {0};
我如何为元素赋值?如在C:
a[2][3] = 3;
答案 0 :(得分:60)
您可以使用哈希模拟它们,但需要关注前导零和许多其他内容。下一个示范工作,但它远非最佳解决方案。
#!/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维。${matrix[2,3]}
答案 1 :(得分:22)
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
修改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
答案 2 :(得分:22)
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
(逗号运算符右侧的值)。
答案 3 :(得分:5)
你也可以用更聪明的方式来解决这个问题
q=()
q+=( 1-2 )
q+=( a-b )
for set in ${q[@]};
do
echo ${set%%-*}
echo ${set##*-}
done
当然,22行解决方案或间接可能是更好的方法,为什么不在每个地方撒上eval。
答案 4 :(得分:4)
一种在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 ###
################
答案 5 :(得分:4)
另一种方法是您可以将每一行表示为字符串,即将2D数组映射到一维数组。然后,您需要做的就是在进行编辑时解压缩并重新打包行的字符串:
# 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
答案 6 :(得分:2)
如果矩阵的每一行都是相同的大小,那么你可以简单地使用线性数组和乘法。
即,
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简直无法做的事情”下提交多维数组。
答案 7 :(得分:1)
可以简单地定义两个要写入的函数($ 4是赋值)并读取具有任意名称($ 1)和索引($ 2和$ 3)的矩阵,利用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
答案 8 :(得分:1)
2D数组,然后可以使用(r * col_size) + c)
访问元素。在逻辑delcares 1D数组(str_2d_arr
)下方,并打印为2D数组。
col_size=3
str_2d_arr=()
str_2d_arr+=('abc' '200' 'xyz')
str_2d_arr+=('def' '300' 'ccc')
str_2d_arr+=('aaa' '400' 'ddd')
echo "Print 2D array"
col_count=0
for elem in ${str_2d_arr[@]}; do
if [ ${col_count} -eq ${col_size} ]; then
echo ""
col_count=0
fi
echo -e "$elem \c"
((col_count++))
done
echo ""
输出为
Print 2D array
abc 200 xyz
def 300 ccc
aaa 400 ddd
下面的逻辑对于从上面声明的1D数组str_2d_arr
中获取每一行非常有用。
# Get nth row and update to 2nd arg
get_row_n()
{
row=$1
local -n a=$2
start_idx=$((row * col_size))
for ((i = 0; i < ${col_size}; i++)); do
idx=$((start_idx + i))
a+=(${str_2d_arr[${idx}]})
done
}
arr=()
get_row_n 0 arr
echo "Row 0"
for e in ${arr[@]}; do
echo -e "$e \c"
done
echo ""
输出为
Row 0
abc 200 xyz
答案 9 :(得分:0)
为了模拟二维数组,我首先加载第一个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
答案 10 :(得分:0)
马克·里德(Mark Reed)提出了一个非常好的2D阵列(矩阵)解决方案!它们始终可以转换为一维数组(向量)。尽管Bash没有对2D数组的本机支持,但围绕所提到的原理创建简单的ADT并不难。
这是一个没有参数检查等的裸露示例,只是为了使解决方案清晰易懂:数组的大小设置为实例中的前两个元素。
#!/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
答案 11 :(得分:0)
如果您在使用bash版本4的Mac上运行,下面的代码肯定可以正常工作。不仅可以声明0,而且这更是一种通用的动态接受值的方法。
declare -A arr
echo "Enter the row"
read r
echo "Enter the column"
read c
i=0
j=0
echo "Enter the elements"
while [ $i -lt $r ]
do
j=0
while [ $j -lt $c ]
do
echo $i $j
read m
arr[${i},${j}]=$m
j=`expr $j + 1`
done
i=`expr $i + 1`
done
i=0
j=0
while [ $i -lt $r ]
do
j=0
while [ $j -lt $c ]
do
echo -n ${arr[${i},${j}]} " "
j=`expr $j + 1`
done
echo ""
i=`expr $i + 1`
done