Bash二维数组赋值异常:为何matrix[x,10]全部被设为1?
问题描述
用declare -a matrix声明一个未初始化的数组,打印规则为:矩阵对应位置值为1则输出1,否则输出_。未设置值时输出正常,但执行matrix[10,10]=1后,所有matrix[x,10]位置的值全变成了1。
附测试代码:
#!/bin/bash #read N declare -a matrix for (( i=0; i<10; i++));do for (( j=0; j<20; j++));do if [[ matrix[$i,$j] -eq 1 ]];then printf 1 else printf _ fi done echo done echo matrix[10,10]=1 for (( i=0; i<10; i++));do for (( j=0; j<20; j++));do if [[ matrix[$i,$j] -eq 1 ]];then printf 1 else printf _ fi done echo done
输出结果:
____________________ ____________________ ____________________ ____________________ ____________________ ____________________ ____________________ ____________________ ____________________ ____________________ __________1_________ __________1_________ __________1_________ __________1_________ __________1_________ __________1_________ __________1_________ __________1_________ __________1_________ __________1_________
原因分析
Bash没有真正的二维数组,declare -a声明的是一维索引数组。当你用matrix[10,10]这种带逗号的下标时,Bash会把逗号当作算术表达式的分隔符——算术表达式里的逗号会依次执行前后的表达式,最终取最后一个表达式的结果作为数组下标。
简单说:matrix[10,10]等价于matrix[10],因为10,10这个算术表达式的结果是最后一个值10;同理所有matrix[x,10]都会被解析成matrix[10]。你给这个位置赋值1后,所有访问x,10的地方都在取matrix[10]的值,自然全变成1了。
另外,你代码里的matrix[$i,$j]少了大括号,正确的取值应该是${matrix[$i,$j]},不过这不是导致问题的核心原因。
解决方法
要在Bash里正确模拟二维矩阵,有两种实用方式:
方式1:用关联数组存字符串坐标
用declare -A声明关联数组(支持字符串作为键),把二维坐标拼接成唯一字符串(比如"$i,$j")作为数组的键,这样每个坐标都是独立的。
修改后的代码:
#!/bin/bash declare -A matrix # 声明为关联数组 for (( i=0; i<10; i++));do for (( j=0; j<20; j++));do # 用引号包裹坐标字符串,避免特殊字符干扰 if [[ ${matrix["$i,$j"]} -eq 1 ]];then printf 1 else printf _ fi done echo done echo matrix["10,10"]=1 # 同样用字符串坐标赋值 for (( i=0; i<10; i++));do for (( j=0; j<20; j++));do if [[ ${matrix["$i,$j"]} -eq 1 ]];then printf 1 else printf _ fi done echo done
方式2:计算一维索引偏移
把二维坐标(i,j)转换成一维数组的索引,公式为:索引 = i * 总列数 + j。这种方式适合用普通索引数组(declare -a),性能略好。
修改后的代码:
#!/bin/bash declare -a matrix cols=20 # 固定列数 for (( i=0; i<10; i++));do for (( j=0; j<cols; j++));do index=$((i * cols + j)) if [[ ${matrix[$index]} -eq 1 ]];then printf 1 else printf _ fi done echo done echo i=10 j=10 index=$((i * cols + j)) matrix[$index]=1 for (( i=0; i<10; i++));do for (( j=0; j<cols; j++));do index=$((i * cols + j)) if [[ ${matrix[$index]} -eq 1 ]];then printf 1 else printf _ fi done echo done
两种方式各有优势:关联数组的写法更直观,适合任意行列数的场景;索引偏移的方式更高效,适合固定行列数的矩阵。
内容的提问来源于stack exchange,提问作者schegga_B

